diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index afe9da084957777e43fa4ec3868ea9c5144565c3..c7ef75d1c32d919d3feb5adeac587b3c2ec38854 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -447,9 +447,8 @@ danger-review:
     - retry gem install danger --no-ri --no-rdoc
   cache: {}
   only:
-    refs:
-      - branches@gitlab-org/gitlab-ce
-      - branches@gitlab-org/gitlab-ee
+    variables:
+      - $DANGER_GITLAB_API_TOKEN
   except:
     refs:
       - master
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76a016b233c72549f54bc9a8634bafffdd2f9a6c..7cc047c40ed9fdae828b637929e26bcc2dd79dd6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,18 @@
 documentation](doc/development/changelog.md) for instructions on adding your own
 entry.
 
+## 11.1.1 (2018-07-23)
+
+### Fixed (2 changes)
+
+- Add missing Gitaly branch_update nil checks. !20711
+- Fix filename for accelerated uploads.
+
+### Added (1 change)
+
+- Add uploader support to Import/Export uploads. !20484
+
+
 ## 11.1.0 (2018-07-22)
 
 ### Security (6 changes)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6056e18595dff4f1cc47d06826381e8b5e4f8103..ad8022e972fd95448c00123e847198a70bea04da 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -133,7 +133,7 @@ Most issues will have labels for at least one of the following:
 
 - Type: ~"feature proposal", ~bug, ~customer, etc.
 - Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
-- Team: ~"CI/CD", ~Plan, ~Quality, ~Platform, etc.
+- Team: ~"CI/CD", ~Plan, ~Manage, ~Quality, etc.
 - Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release"
 - Priority: ~P1, ~P2, ~P3, ~P4
 - Severity: ~S1, ~S2, ~S3, ~S4
@@ -192,9 +192,9 @@ The current team labels are:
 - ~Documentation
 - ~Geo
 - ~Gitaly
+- ~Manage
 - ~Monitoring
 - ~Plan
-- ~Platform
 - ~Quality
 - ~Release
 - ~"Security Products"
@@ -376,8 +376,14 @@ on those issues. Please select someone with relevant experience from the
 [GitLab team][team]. If there is nobody mentioned with that expertise look in
 the commit history for the affected files to find someone.
 
+We also use [GitLab Triage] to automate some triaging policies. This is
+currently setup as a [scheduled pipeline] running on the [`gl-triage`] branch.
+
 [described in our handbook]: https://about.gitlab.com/handbook/engineering/issue-triage/
 [issue bash events]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17815
+[GitLab Triage]: https://gitlab.com/gitlab-org/gitlab-triage
+[scheduled pipeline]: https://gitlab.com/gitlab-org/gitlab-ce/pipeline_schedules/3732/edit
+[`gl-triage`]: https://gitlab.com/gitlab-org/gitlab-ce/tree/gl-triage
 
 ### Feature proposals
 
diff --git a/Dangerfile b/Dangerfile
index 84b72673c50470170e91c3791f467a5e39047b0d..9217610da8bf26103fc699bc60718e19a3dd5e15 100644
--- a/Dangerfile
+++ b/Dangerfile
@@ -4,3 +4,4 @@ danger.import_dangerfile(path: 'danger/changelog')
 danger.import_dangerfile(path: 'danger/specs')
 danger.import_dangerfile(path: 'danger/gemfile')
 danger.import_dangerfile(path: 'danger/database')
+danger.import_dangerfile(path: 'danger/frozen_string')
diff --git a/Gemfile b/Gemfile
index 0ad5df90c3cfcf101aa5cadcebf746c2c8d70194..74e9625d57b1150988416bf5abd8e701fb854ed5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -220,6 +220,9 @@ gem 'gemnasium-gitlab-service', '~> 0.2'
 # Slack integration
 gem 'slack-notifier', '~> 1.5.1'
 
+# Hangouts Chat integration
+gem 'hangouts-chat', '~> 0.0.5'
+
 # Asana integration
 gem 'asana', '~> 0.6.0'
 
@@ -230,7 +233,7 @@ gem 'ruby-fogbugz', '~> 0.2.1'
 gem 'kubeclient', '~> 3.1.0'
 
 # Sanitize user input
-gem 'sanitize', '~> 4.6.5'
+gem 'sanitize', '~> 4.6'
 gem 'babosa', '~> 1.0.2'
 
 # Sanitizes SVG input
@@ -419,7 +422,7 @@ group :ed25519 do
 end
 
 # Gitaly GRPC client
-gem 'gitaly-proto', '~> 0.106.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 0.109.0', require: 'gitaly'
 gem 'grpc', '~> 1.11.0'
 
 # Locked until https://github.com/google/protobuf/issues/4210 is closed
diff --git a/Gemfile.lock b/Gemfile.lock
index 1d9c75f154ca26b4c12a4cc652a95cf68f1037b0..5bb7bc539625bc0746a1ff1cd8fc5478bc417808 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -284,7 +284,7 @@ GEM
       gettext_i18n_rails (>= 0.7.1)
       po_to_json (>= 1.0.0)
       rails (>= 3.2.0)
-    gitaly-proto (0.106.0)
+    gitaly-proto (0.109.0)
       google-protobuf (~> 3.1)
       grpc (~> 1.10)
     github-linguist (5.3.3)
@@ -387,6 +387,7 @@ GEM
       temple (>= 0.8.0)
       thor
       tilt
+    hangouts-chat (0.0.5)
     hashdiff (0.3.4)
     hashie (3.5.7)
     hashie-forbidden_attributes (0.1.1)
@@ -396,7 +397,7 @@ GEM
     hipchat (1.5.2)
       httparty
       mimemagic
-    html-pipeline (2.8.3)
+    html-pipeline (2.8.4)
       activesupport (>= 2)
       nokogiri (>= 1.4)
     html2text (0.2.0)
@@ -513,7 +514,7 @@ GEM
     net-ldap (0.16.0)
     net-ssh (5.0.1)
     netrc (0.11.0)
-    nokogiri (1.8.3)
+    nokogiri (1.8.4)
       mini_portile2 (~> 2.3.0)
     nokogumbo (1.5.0)
       nokogiri
@@ -807,7 +808,7 @@ GEM
       et-orbi (~> 1.0)
     rugged (0.27.2)
     safe_yaml (1.0.4)
-    sanitize (4.6.5)
+    sanitize (4.6.6)
       crass (~> 1.0.2)
       nokogiri (>= 1.4.4)
       nokogumbo (~> 1.4)
@@ -1041,7 +1042,7 @@ DEPENDENCIES
   gettext (~> 3.2.2)
   gettext_i18n_rails (~> 1.8.0)
   gettext_i18n_rails_js (~> 1.3)
-  gitaly-proto (~> 0.106.0)
+  gitaly-proto (~> 0.109.0)
   github-linguist (~> 5.3.3)
   gitlab-flowdock-git-hook (~> 1.0.1)
   gitlab-gollum-lib (~> 4.2)
@@ -1062,6 +1063,7 @@ DEPENDENCIES
   grpc (~> 1.11.0)
   haml_lint (~> 0.26.0)
   hamlit (~> 2.8.8)
+  hangouts-chat (~> 0.0.5)
   hashie-forbidden_attributes
   health_check (~> 2.6.0)
   hipchat (~> 1.5.0)
@@ -1155,7 +1157,7 @@ DEPENDENCIES
   ruby_parser (~> 3.8)
   rufus-scheduler (~> 3.4)
   rugged (~> 0.27)
-  sanitize (~> 4.6.5)
+  sanitize (~> 4.6)
   sass-rails (~> 5.0.6)
   scss_lint (~> 0.56.0)
   seed-fu (~> 2.3.7)
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index 2d12faa28d907ea851b899107a3ae8a2d721d12d..8c14316a67a1d15476f00e444b7b1a23166a7453 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -287,7 +287,7 @@ GEM
       gettext_i18n_rails (>= 0.7.1)
       po_to_json (>= 1.0.0)
       rails (>= 3.2.0)
-    gitaly-proto (0.106.0)
+    gitaly-proto (0.109.0)
       google-protobuf (~> 3.1)
       grpc (~> 1.10)
     github-linguist (5.3.3)
@@ -390,6 +390,7 @@ GEM
       temple (>= 0.8.0)
       thor
       tilt
+    hangouts-chat (0.0.5)
     hashdiff (0.3.4)
     hashie (3.5.7)
     hashie-forbidden_attributes (0.1.1)
@@ -1051,7 +1052,7 @@ DEPENDENCIES
   gettext (~> 3.2.2)
   gettext_i18n_rails (~> 1.8.0)
   gettext_i18n_rails_js (~> 1.3)
-  gitaly-proto (~> 0.106.0)
+  gitaly-proto (~> 0.109.0)
   github-linguist (~> 5.3.3)
   gitlab-flowdock-git-hook (~> 1.0.1)
   gitlab-gollum-lib (~> 4.2)
@@ -1072,6 +1073,7 @@ DEPENDENCIES
   grpc (~> 1.11.0)
   haml_lint (~> 0.26.0)
   hamlit (~> 2.8.8)
+  hangouts-chat (~> 0.0.5)
   hashie-forbidden_attributes
   health_check (~> 2.6.0)
   hipchat (~> 1.5.0)
@@ -1166,7 +1168,7 @@ DEPENDENCIES
   ruby_parser (~> 3.8)
   rufus-scheduler (~> 3.4)
   rugged (~> 0.27)
-  sanitize (~> 4.6.5)
+  sanitize (~> 4.6)
   sass-rails (~> 5.0.6)
   scss_lint (~> 0.56.0)
   seed-fu (~> 2.3.7)
diff --git a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
index 0fe0007057b38739d23fa05ec1fb400e1e38e7b8..d184a76f0386fb3e07d680245fb291beabcebecd 100644
--- a/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_gutter_content.vue
@@ -71,6 +71,11 @@ export default {
       required: false,
       default: false,
     },
+    discussions: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
   },
   computed: {
     ...mapState({
@@ -78,7 +83,6 @@ export default {
       diffFiles: state => state.diffs.diffFiles,
     }),
     ...mapGetters(['isLoggedIn']),
-    ...mapGetters('diffs', ['discussionsByLineCode']),
     lineHref() {
       return this.lineCode ? `#${this.lineCode}` : '#';
     },
@@ -88,24 +92,19 @@ export default {
         this.showCommentButton &&
         !this.isMatchLine &&
         !this.isContextLine &&
-        !this.hasDiscussions &&
-        !this.isMetaLine
+        !this.isMetaLine &&
+        !this.hasDiscussions
       );
     },
-    discussions() {
-      return this.discussionsByLineCode[this.lineCode] || [];
-    },
     hasDiscussions() {
       return this.discussions.length > 0;
     },
     shouldShowAvatarsOnGutter() {
-      let render = this.hasDiscussions && this.showCommentButton;
-
       if (!this.lineType && this.linePosition === LINE_POSITION_RIGHT) {
-        render = false;
+        return false;
       }
 
-      return render;
+      return this.hasDiscussions && this.showCommentButton;
     },
   },
   methods: {
diff --git a/app/assets/javascripts/diffs/components/diff_table_cell.vue b/app/assets/javascripts/diffs/components/diff_table_cell.vue
index 5962f30d9bb1922c72aa2ce6d6ff38ff86154907..e8e8ddc6c5e7d9fc6809b35a86600bef17b68f34 100644
--- a/app/assets/javascripts/diffs/components/diff_table_cell.vue
+++ b/app/assets/javascripts/diffs/components/diff_table_cell.vue
@@ -67,6 +67,11 @@ export default {
       required: false,
       default: false,
     },
+    discussions: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
   },
   computed: {
     ...mapGetters(['isLoggedIn']),
@@ -136,6 +141,7 @@ export default {
       :is-match-line="isMatchLine"
       :is-context-line="isContentLine"
       :is-meta-line="isMetaLine"
+      :discussions="discussions"
     />
   </td>
 </template>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
index a6f011ff31e4dd3ec41ef74bec7203bca91bf081..1b5ae5e9f75b0583a80ff17cfe4e62c1eb1dd0a3 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_comment_row.vue
@@ -1,5 +1,5 @@
 <script>
-import { mapState, mapGetters } from 'vuex';
+import { mapState } from 'vuex';
 import diffDiscussions from './diff_discussions.vue';
 import diffLineNoteForm from './diff_line_note_form.vue';
 
@@ -21,15 +21,16 @@ export default {
       type: Number,
       required: true,
     },
+    discussions: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
   },
   computed: {
     ...mapState({
       diffLineCommentForms: state => state.diffs.diffLineCommentForms,
     }),
-    ...mapGetters('diffs', ['discussionsByLineCode']),
-    discussions() {
-      return this.discussionsByLineCode[this.line.lineCode] || [];
-    },
     className() {
       return this.discussions.length ? '' : 'js-temp-notes-holder';
     },
diff --git a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
index 0e306f39a9f9c32326b53c25277cc3a8c2bd88aa..32d65ff994ff522f411ac8ed89d420d4bfcfd554 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_table_row.vue
@@ -33,6 +33,11 @@ export default {
       required: false,
       default: false,
     },
+    discussions: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
   },
   data() {
     return {
@@ -89,6 +94,7 @@ export default {
       :is-bottom="isBottom"
       :is-hover="isHover"
       :show-comment-button="true"
+      :discussions="discussions"
       class="diff-line-num old_line"
     />
     <diff-table-cell
@@ -98,6 +104,7 @@ export default {
       :line-type="newLineType"
       :is-bottom="isBottom"
       :is-hover="isHover"
+      :discussions="discussions"
       class="diff-line-num new_line"
     />
     <td
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index 8e491d293e5f83efba04eb92135e4fa332aefd92..5f30cc57a5962d8cc3fbaa103ad2c511358e386d 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -20,7 +20,11 @@ export default {
     },
   },
   computed: {
-    ...mapGetters('diffs', ['commitId', 'discussionsByLineCode']),
+    ...mapGetters('diffs', [
+      'commitId',
+      'shouldRenderInlineCommentRow',
+      'singleDiscussionByLineCode',
+    ]),
     ...mapState({
       diffLineCommentForms: state => state.diffs.diffLineCommentForms,
     }),
@@ -34,18 +38,7 @@ export default {
       return window.gon.user_color_scheme;
     },
   },
-  methods: {
-    shouldRenderCommentRow(line) {
-      if (this.diffLineCommentForms[line.lineCode]) return true;
-
-      const lineDiscussions = this.discussionsByLineCode[line.lineCode];
-      if (lineDiscussions === undefined) {
-        return false;
-      }
-
-      return lineDiscussions.every(discussion => discussion.expanded);
-    },
-  },
+  methods: {},
 };
 </script>
 
@@ -64,13 +57,15 @@ export default {
           :line="line"
           :is-bottom="index + 1 === diffLinesLength"
           :key="line.lineCode"
+          :discussions="singleDiscussionByLineCode(line.lineCode)"
         />
         <inline-diff-comment-row
-          v-if="shouldRenderCommentRow(line)"
+          v-if="shouldRenderInlineCommentRow(line)"
           :diff-file-hash="diffFile.fileHash"
           :line="line"
           :line-index="index"
           :key="index"
+          :discussions="singleDiscussionByLineCode(line.lineCode)"
         />
       </template>
     </tbody>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
index 05e5cafc717188d9a8db12399624a0bc989bec30..bb9a65c83fad2d832abe20abf4b2998782ef0945 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_comment_row.vue
@@ -1,5 +1,5 @@
 <script>
-import { mapState, mapGetters } from 'vuex';
+import { mapState } from 'vuex';
 import diffDiscussions from './diff_discussions.vue';
 import diffLineNoteForm from './diff_line_note_form.vue';
 
@@ -21,48 +21,51 @@ export default {
       type: Number,
       required: true,
     },
+    leftDiscussions: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
+    rightDiscussions: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
   },
   computed: {
     ...mapState({
       diffLineCommentForms: state => state.diffs.diffLineCommentForms,
     }),
-    ...mapGetters('diffs', ['discussionsByLineCode']),
     leftLineCode() {
       return this.line.left.lineCode;
     },
     rightLineCode() {
       return this.line.right.lineCode;
     },
-    hasDiscussion() {
-      const discussions = this.discussionsByLineCode;
-
-      return discussions[this.leftLineCode] || discussions[this.rightLineCode];
-    },
     hasExpandedDiscussionOnLeft() {
-      const discussions = this.discussionsByLineCode[this.leftLineCode];
-
+      const discussions = this.leftDiscussions;
       return discussions ? discussions.every(discussion => discussion.expanded) : false;
     },
     hasExpandedDiscussionOnRight() {
-      const discussions = this.discussionsByLineCode[this.rightLineCode];
-
+      const discussions = this.rightDiscussions;
       return discussions ? discussions.every(discussion => discussion.expanded) : false;
     },
     hasAnyExpandedDiscussion() {
       return this.hasExpandedDiscussionOnLeft || this.hasExpandedDiscussionOnRight;
     },
     shouldRenderDiscussionsOnLeft() {
-      return this.discussionsByLineCode[this.leftLineCode] && this.hasExpandedDiscussionOnLeft;
+      return this.leftDiscussions && this.hasExpandedDiscussionOnLeft;
     },
     shouldRenderDiscussionsOnRight() {
-      return (
-        this.discussionsByLineCode[this.rightLineCode] &&
-        this.hasExpandedDiscussionOnRight &&
-        this.line.right.type
-      );
+      return this.rightDiscussions && this.hasExpandedDiscussionOnRight && this.line.right.type;
+    },
+    showRightSideCommentForm() {
+      return this.line.right.type && this.diffLineCommentForms[this.rightLineCode];
     },
     className() {
-      return this.hasDiscussion ? '' : 'js-temp-notes-holder';
+      return this.leftDiscussions.length > 0 || this.rightDiscussions.length > 0
+        ? ''
+        : 'js-temp-notes-holder';
     },
   },
 };
@@ -80,13 +83,12 @@ export default {
         class="content"
       >
         <diff-discussions
-          v-if="discussionsByLineCode[leftLineCode].length"
-          :discussions="discussionsByLineCode[leftLineCode]"
+          v-if="leftDiscussions.length"
+          :discussions="leftDiscussions"
         />
       </div>
       <diff-line-note-form
-        v-if="diffLineCommentForms[leftLineCode] &&
-        diffLineCommentForms[leftLineCode]"
+        v-if="diffLineCommentForms[leftLineCode]"
         :diff-file-hash="diffFileHash"
         :line="line.left"
         :note-target-line="line.left"
@@ -100,13 +102,12 @@ export default {
         class="content"
       >
         <diff-discussions
-          v-if="discussionsByLineCode[rightLineCode].length"
-          :discussions="discussionsByLineCode[rightLineCode]"
+          v-if="rightDiscussions.length"
+          :discussions="rightDiscussions"
         />
       </div>
       <diff-line-note-form
-        v-if="diffLineCommentForms[rightLineCode] &&
-        diffLineCommentForms[rightLineCode] && line.right.type"
+        v-if="showRightSideCommentForm"
         :diff-file-hash="diffFileHash"
         :line="line.right"
         :note-target-line="line.right"
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
index 0031cedc68f9a4731c84dbb3373e17201ca82b55..d4e54c2bd00c266736acf478117a7d2920f78218 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -36,6 +36,16 @@ export default {
       required: false,
       default: false,
     },
+    leftDiscussions: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
+    rightDiscussions: {
+      type: Array,
+      required: false,
+      default: () => [],
+    },
   },
   data() {
     return {
@@ -116,6 +126,7 @@ export default {
       :is-hover="isLeftHover"
       :show-comment-button="true"
       :diff-view-type="parallelDiffViewType"
+      :discussions="leftDiscussions"
       class="diff-line-num old_line"
     />
     <td
@@ -136,6 +147,7 @@ export default {
       :is-hover="isRightHover"
       :show-comment-button="true"
       :diff-view-type="parallelDiffViewType"
+      :discussions="rightDiscussions"
       class="diff-line-num new_line"
     />
     <td
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index 8f8d6bbc81825ae7a0867423aa472b3812d6d259..4d97cb6d15dd0a20ec0e07a79bf48fc8d61c89c5 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -21,7 +21,11 @@ export default {
     },
   },
   computed: {
-    ...mapGetters('diffs', ['commitId', 'discussionsByLineCode']),
+    ...mapGetters('diffs', [
+      'commitId',
+      'singleDiscussionByLineCode',
+      'shouldRenderParallelCommentRow',
+    ]),
     ...mapState({
       diffLineCommentForms: state => state.diffs.diffLineCommentForms,
     }),
@@ -51,32 +55,6 @@ export default {
       return window.gon.user_color_scheme;
     },
   },
-  methods: {
-    shouldRenderCommentRow(line) {
-      const leftLineCode = line.left.lineCode;
-      const rightLineCode = line.right.lineCode;
-      const discussions = this.discussionsByLineCode;
-      const leftDiscussions = discussions[leftLineCode];
-      const rightDiscussions = discussions[rightLineCode];
-      const hasDiscussion = leftDiscussions || rightDiscussions;
-
-      const hasExpandedDiscussionOnLeft = leftDiscussions
-        ? leftDiscussions.every(discussion => discussion.expanded)
-        : false;
-      const hasExpandedDiscussionOnRight = rightDiscussions
-        ? rightDiscussions.every(discussion => discussion.expanded)
-        : false;
-
-      if (hasDiscussion && (hasExpandedDiscussionOnLeft || hasExpandedDiscussionOnRight)) {
-        return true;
-      }
-
-      const hasCommentFormOnLeft = this.diffLineCommentForms[leftLineCode];
-      const hasCommentFormOnRight = this.diffLineCommentForms[rightLineCode];
-
-      return hasCommentFormOnLeft || hasCommentFormOnRight;
-    },
-  },
 };
 </script>
 
@@ -97,13 +75,17 @@ export default {
             :line="line"
             :is-bottom="index + 1 === diffLinesLength"
             :key="index"
+            :left-discussions="singleDiscussionByLineCode(line.left.lineCode)"
+            :right-discussions="singleDiscussionByLineCode(line.right.lineCode)"
           />
           <parallel-diff-comment-row
-            v-if="shouldRenderCommentRow(line)"
+            v-if="shouldRenderParallelCommentRow(line)"
             :key="`dcr-${index}`"
             :line="line"
             :diff-file-hash="diffFile.fileHash"
             :line-index="index"
+            :left-discussions="singleDiscussionByLineCode(line.left.lineCode)"
+            :right-discussions="singleDiscussionByLineCode(line.right.lineCode)"
           />
         </template>
       </tbody>
diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js
index d3881fa1a0a55c0db256b23329b62f2af69dcbbb..c7b9b1a16e6ca43be7ce2186fddb4cbf02fab88d 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -75,19 +75,21 @@ export const discussionsByLineCode = (state, getters, rootState, rootGetters) =>
     const isDiffDiscussion = note.diff_discussion;
     const hasLineCode = note.line_code;
     const isResolvable = note.resolvable;
-    const diffRefs = diffRefsByLineCode[note.line_code];
 
-    if (isDiffDiscussion && hasLineCode && isResolvable && diffRefs) {
-      const refs = convertObjectPropsToCamelCase(note.position.formatter);
-      const originalRefs = convertObjectPropsToCamelCase(note.original_position.formatter);
+    if (isDiffDiscussion && hasLineCode && isResolvable) {
+      const diffRefs = diffRefsByLineCode[note.line_code];
+      if (diffRefs) {
+        const refs = convertObjectPropsToCamelCase(note.position.formatter);
+        const originalRefs = convertObjectPropsToCamelCase(note.original_position.formatter);
 
-      if (_.isEqual(refs, diffRefs) || _.isEqual(originalRefs, diffRefs)) {
-        const lineCode = note.line_code;
+        if (_.isEqual(refs, diffRefs) || _.isEqual(originalRefs, diffRefs)) {
+          const lineCode = note.line_code;
 
-        if (acc[lineCode]) {
-          acc[lineCode].push(note);
-        } else {
-          acc[lineCode] = [note];
+          if (acc[lineCode]) {
+            acc[lineCode].push(note);
+          } else {
+            acc[lineCode] = [note];
+          }
         }
       }
     }
@@ -96,6 +98,47 @@ export const discussionsByLineCode = (state, getters, rootState, rootGetters) =>
   }, {});
 };
 
+export const singleDiscussionByLineCode = (state, getters) => lineCode => {
+  if (!lineCode) return [];
+  const discussions = getters.discussionsByLineCode;
+  return discussions[lineCode] || [];
+};
+
+export const shouldRenderParallelCommentRow = (state, getters) => line => {
+  const leftLineCode = line.left.lineCode;
+  const rightLineCode = line.right.lineCode;
+  const leftDiscussions = getters.singleDiscussionByLineCode(leftLineCode);
+  const rightDiscussions = getters.singleDiscussionByLineCode(rightLineCode);
+  const hasDiscussion = leftDiscussions.length || rightDiscussions.length;
+
+  const hasExpandedDiscussionOnLeft = leftDiscussions.length
+    ? leftDiscussions.every(discussion => discussion.expanded)
+    : false;
+  const hasExpandedDiscussionOnRight = rightDiscussions.length
+    ? rightDiscussions.every(discussion => discussion.expanded)
+    : false;
+
+  if (hasDiscussion && (hasExpandedDiscussionOnLeft || hasExpandedDiscussionOnRight)) {
+    return true;
+  }
+
+  const hasCommentFormOnLeft = state.diffLineCommentForms[leftLineCode];
+  const hasCommentFormOnRight = state.diffLineCommentForms[rightLineCode];
+
+  return hasCommentFormOnLeft || hasCommentFormOnRight;
+};
+
+export const shouldRenderInlineCommentRow = (state, getters) => line => {
+  if (state.diffLineCommentForms[line.lineCode]) return true;
+
+  const lineDiscussions = getters.singleDiscussionByLineCode(line.lineCode);
+  if (lineDiscussions.length === 0) {
+    return false;
+  }
+
+  return lineDiscussions.every(discussion => discussion.expanded);
+};
+
 // prevent babel-plugin-rewire from generating an invalid default during karma∂ tests
 export const getDiffFileByHash = state => fileHash =>
   state.diffFiles.find(file => file.fileHash === fileHash);
diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js
index 029fd6a67d444e52b63660b3657ec7f321dddd30..efba6fc1affa935fc67a0ce16bfe353258398f65 100644
--- a/app/assets/javascripts/gpg_badges.js
+++ b/app/assets/javascripts/gpg_badges.js
@@ -1,23 +1,36 @@
 import $ from 'jquery';
 import { parseQueryStringIntoObject } from '~/lib/utils/common_utils';
 import axios from '~/lib/utils/axios_utils';
-import flash from '~/flash';
+import createFlash from '~/flash';
 import { __ } from '~/locale';
 
 export default class GpgBadges {
   static fetch() {
-    const badges = $('.js-loading-gpg-badge');
     const tag = $('.js-signature-container');
+    if (tag.length === 0) {
+      return Promise.resolve();
+    }
+
+    const badges = $('.js-loading-gpg-badge');
 
     badges.html('<i class="fa fa-spinner fa-spin"></i>');
 
+    const displayError = () => createFlash(__('An error occurred while loading commit signatures'));
+
+    const endpoint = tag.data('signaturesPath');
+    if (!endpoint) {
+      displayError();
+      return Promise.reject(new Error('Missing commit signatures endpoint!'));
+    }
+
     const params = parseQueryStringIntoObject(tag.serialize());
-    return axios.get(tag.data('signaturesPath'), { params })
-    .then(({ data }) => {
-      data.signatures.forEach((signature) => {
-        badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html);
-      });
-    })
-    .catch(() => flash(__('An error occurred while loading commits')));
+    return axios
+      .get(endpoint, { params })
+      .then(({ data }) => {
+        data.signatures.forEach(signature => {
+          badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html);
+        });
+      })
+      .catch(displayError);
   }
 }
diff --git a/app/assets/javascripts/issue_show/components/edited.vue b/app/assets/javascripts/issue_show/components/edited.vue
index 5ff5b1630b1e2fae40906698b34893be2497012e..05cd976f1968bbfd66b5bba2f548bdb857154c09 100644
--- a/app/assets/javascripts/issue_show/components/edited.vue
+++ b/app/assets/javascripts/issue_show/components/edited.vue
@@ -46,7 +46,7 @@
       by
       <a
         :href="updatedByPath"
-        class="author_link"
+        class="author-link"
       >
         <span>{{ updatedByName }}</span>
       </a>
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 6b7550efff8b56921825aa1e1566f499b1a942fe..2f3dd6f6cbc7286a6fa602b89b9d282bfbd75170 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -541,6 +541,26 @@ export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => {
   });
 };
 
+/**
+ * Method to round of values with decimal places
+ * with provided precision.
+ *
+ * Taken from https://stackoverflow.com/a/7343013/414749
+ *
+ * Eg; roundOffFloat(3.141592, 3) = 3.142
+ *
+ * Refer to spec/javascripts/lib/utils/common_utils_spec.js for
+ * more supported examples.
+ *
+ * @param {Float} number
+ * @param {Number} precision
+ */
+export const roundOffFloat = (number, precision = 0) => {
+  // eslint-disable-next-line no-restricted-properties
+  const multiplier = Math.pow(10, precision);
+  return Math.round(number * multiplier) / multiplier;
+};
+
 window.gl = window.gl || {};
 window.gl.utils = {
   ...(window.gl.utils || {}),
diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js
index 7fca80c2fdb3207463402681416c2b6869b21b5b..91d8c30744fc1153d52b531ec70368ba0479fd05 100644
--- a/app/assets/javascripts/lib/utils/poll.js
+++ b/app/assets/javascripts/lib/utils/poll.js
@@ -38,7 +38,7 @@ import { normalizeHeaders } from './common_utils';
  *  } else {
  *   poll.stop();
  *  }
-* });
+ * });
  *
  * 1. Checks for response and headers before start polling
  * 2. Interval is provided by `Poll-Interval` header.
@@ -51,8 +51,8 @@ export default class Poll {
   constructor(options = {}) {
     this.options = options;
     this.options.data = options.data || {};
-    this.options.notificationCallback = options.notificationCallback ||
-      function notificationCallback() {};
+    this.options.notificationCallback =
+      options.notificationCallback || function notificationCallback() {};
 
     this.intervalHeader = 'POLL-INTERVAL';
     this.timeoutID = null;
@@ -63,6 +63,7 @@ export default class Poll {
     const headers = normalizeHeaders(response.headers);
     const pollInterval = parseInt(headers[this.intervalHeader], 10);
     if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
+      clearTimeout(this.timeoutID);
       this.timeoutID = setTimeout(() => {
         this.makeRequest();
       }, pollInterval);
@@ -77,11 +78,11 @@ export default class Poll {
     notificationCallback(true);
 
     return resource[method](data)
-      .then((response) => {
+      .then(response => {
         this.checkConditions(response);
         notificationCallback(false);
       })
-      .catch((error) => {
+      .catch(error => {
         notificationCallback(false);
         if (error.status === httpStatusCodes.ABORTED) {
           return;
diff --git a/app/assets/javascripts/notes/components/note_edited_text.vue b/app/assets/javascripts/notes/components/note_edited_text.vue
index 391bb2ae179549fffede739c0fe59bd879d743e0..d848335022f00685563a135c1273e0918126bc8d 100644
--- a/app/assets/javascripts/notes/components/note_edited_text.vue
+++ b/app/assets/javascripts/notes/components/note_edited_text.vue
@@ -42,7 +42,7 @@ export default {
       by
       <a
         :href="editedBy.path"
-        class="js-vue-author author_link">
+        class="js-vue-author author-link">
         {{ editedBy.name }}
       </a>
     </template>
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index 6dd4c9d66acb975f1b62bfa2d6dda6f9339d4f42..3aef30c608c002763274977c3ebb89c48afb2c6c 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -15,7 +15,7 @@ document.addEventListener('DOMContentLoaded', () => {
       const notesDataset = document.getElementById('js-vue-notes').dataset;
       const parsedUserData = JSON.parse(notesDataset.currentUserData);
       const noteableData = JSON.parse(notesDataset.noteableData);
-      const { markdownVersion } = notesDataset;
+      const markdownVersion = parseInt(notesDataset.markdownVersion, 10);
       let currentUserData = {};
 
       noteableData.noteableType = notesDataset.noteableType;
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index ab6a95e2601bb945dca326598b6f421f9e938310..e1b159142c940d8ff9e04b2d5b9777c48d4a44d0 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -174,27 +174,19 @@ export default {
 
   [types.UPDATE_NOTE](state, note) {
     const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id);
-
     if (noteObj.individual_note) {
       noteObj.notes.splice(0, 1, note);
     } else {
       const comment = utils.findNoteObjectById(noteObj.notes, note.id);
-      noteObj.notes.splice(noteObj.notes.indexOf(comment), 1, note);
+      Object.assign(comment, note);
     }
   },
 
   [types.UPDATE_DISCUSSION](state, noteData) {
     const note = noteData;
-    let index = 0;
-
-    state.discussions.forEach((n, i) => {
-      if (n.id === note.id) {
-        index = i;
-      }
-    });
-
+    const selectedDiscussion = state.discussions.find(n => n.id === note.id);
     note.expanded = true; // override expand flag to prevent collapse
-    state.discussions.splice(index, 1, note);
+    Object.assign(selectedDiscussion, note);
   },
 
   [types.CLOSE_ISSUE](state) {
@@ -215,12 +207,9 @@ export default {
 
   [types.SET_DISCUSSION_DIFF_LINES](state, { discussionId, diffLines }) {
     const discussion = utils.findNoteObjectById(state.discussions, discussionId);
-    const index = state.discussions.indexOf(discussion);
 
-    const discussionWithDiffLines = Object.assign({}, discussion, {
+    Object.assign(discussion, {
       truncated_diff_lines: diffLines,
     });
-
-    state.discussions.splice(index, 1, discussionWithDiffLines);
   },
 };
diff --git a/app/assets/javascripts/notes/stores/utils.js b/app/assets/javascripts/notes/stores/utils.js
index a0e096ebfafa8e5077d9802236204a36cde69be6..c4a812c5af437d658bd2bc97a4238f5455f4f25f 100644
--- a/app/assets/javascripts/notes/stores/utils.js
+++ b/app/assets/javascripts/notes/stores/utils.js
@@ -2,13 +2,11 @@ import AjaxCache from '~/lib/utils/ajax_cache';
 
 const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
 
-export const findNoteObjectById = (notes, id) =>
-  notes.filter(n => n.id === id)[0];
+export const findNoteObjectById = (notes, id) => notes.find(n => n.id === id);
 
 export const getQuickActionText = note => {
   let text = 'Applying command';
-  const quickActions =
-    AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
+  const quickActions = AjaxCache.get(gl.GfmAutoComplete.dataSources.commands) || [];
 
   const executedCommands = quickActions.filter(command => {
     const commandRegex = new RegExp(`/${command.name}`);
@@ -29,5 +27,4 @@ export const getQuickActionText = note => {
 
 export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note);
 
-export const stripQuickActions = note =>
-  note.replace(REGEX_QUICK_ACTIONS, '').trim();
+export const stripQuickActions = note => note.replace(REGEX_QUICK_ACTIONS, '').trim();
diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js
index 85c6862d629bcbdf053be8bc0898397ea5624412..84e5bb3c46e271c8639014cb4b3f27af72e815fd 100644
--- a/app/assets/javascripts/pages/projects/blob/show/index.js
+++ b/app/assets/javascripts/pages/projects/blob/show/index.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
 import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
 import BlobViewer from '~/blob/viewer/index';
 import initBlob from '~/pages/projects/init_blob';
+import GpgBadges from '~/gpg_badges';
 
 document.addEventListener('DOMContentLoaded', () => {
   new BlobViewer(); // eslint-disable-line no-new
@@ -26,4 +27,6 @@ document.addEventListener('DOMContentLoaded', () => {
       },
     });
   }
+
+  GpgBadges.fetch();
 });
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index 3b0f0f960b82f88c8b00c4d265613ca6220024f5..d2dc0c4570e91bf840ae3f3300cd384bb2a9c9f5 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -7,6 +7,7 @@ import TreeView from '~/tree';
 import BlobViewer from '~/blob/viewer/index';
 import Activities from '~/activities';
 import { ajaxGet } from '~/lib/utils/common_utils';
+import GpgBadges from '~/gpg_badges';
 import Star from '../../../star';
 import notificationsDropdown from '../../../notifications_dropdown';
 
@@ -38,4 +39,6 @@ document.addEventListener('DOMContentLoaded', () => {
   $(treeSlider).waitForImages(() => {
     ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
   });
+
+  GpgBadges.fetch();
 });
diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js
index 7ad082a5e6167cc050477c02eb27005af2845979..33d69d891d847f6f85b22845124ad8b84ad4ac6f 100644
--- a/app/assets/javascripts/pages/projects/tree/show/index.js
+++ b/app/assets/javascripts/pages/projects/tree/show/index.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
 import Vue from 'vue';
 import initBlob from '~/blob_edit/blob_bundle';
 import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
+import GpgBadges from '~/gpg_badges';
 import TreeView from '../../../../tree';
 import ShortcutsNavigation from '../../../../shortcuts_navigation';
 import BlobViewer from '../../../../blob/viewer';
@@ -14,7 +15,8 @@ document.addEventListener('DOMContentLoaded', () => {
   new BlobViewer(); // eslint-disable-line no-new
   new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new
   $('#tree-slider').waitForImages(() =>
-    ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath));
+    ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath),
+  );
 
   initBlob();
   const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status');
@@ -36,4 +38,6 @@ document.addEventListener('DOMContentLoaded', () => {
       },
     });
   }
+
+  GpgBadges.fetch();
 });
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index a4c7c143e5618272e67f19ce2d66eda474048127..1c1e17563a13e59a74983a5c094da3f387a5dbcd 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -1,27 +1,27 @@
 <script>
-  import Visibility from 'visibilityjs';
-  import ciIcon from '~/vue_shared/components/ci_icon.vue';
-  import loadingIcon from '~/vue_shared/components/loading_icon.vue';
-  import Poll from '~/lib/utils/poll';
-  import Flash from '~/flash';
-  import { s__, sprintf } from '~/locale';
-  import tooltip from '~/vue_shared/directives/tooltip';
-  import CommitPipelineService from '../services/commit_pipeline_service';
+import Visibility from 'visibilityjs';
+import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+import Poll from '~/lib/utils/poll';
+import Flash from '~/flash';
+import { s__, sprintf } from '~/locale';
+import tooltip from '~/vue_shared/directives/tooltip';
+import CommitPipelineService from '../services/commit_pipeline_service';
 
-  export default {
-    directives: {
-      tooltip,
+export default {
+  directives: {
+    tooltip,
+  },
+  components: {
+    ciIcon,
+    loadingIcon,
+  },
+  props: {
+    endpoint: {
+      type: String,
+      required: true,
     },
-    components: {
-      ciIcon,
-      loadingIcon,
-    },
-    props: {
-      endpoint: {
-        type: String,
-        required: true,
-      },
-      /* This prop can be used to replace some of the `render_commit_status`
+    /* This prop can be used to replace some of the `render_commit_status`
       used across GitLab, this way we could use this vue component and add a
       realtime status where it makes sense
       realtime: {
@@ -29,76 +29,77 @@
         required: false,
         default: true,
       }, */
+  },
+  data() {
+    return {
+      ciStatus: {},
+      isLoading: true,
+    };
+  },
+  computed: {
+    statusTitle() {
+      return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text });
     },
-    data() {
-      return {
-        ciStatus: {},
-        isLoading: true,
-      };
-    },
-    computed: {
-      statusTitle() {
-        return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text });
-      },
+  },
+  mounted() {
+    this.service = new CommitPipelineService(this.endpoint);
+    this.initPolling();
+  },
+  methods: {
+    successCallback(res) {
+      const { pipelines } = res.data;
+      if (pipelines.length > 0) {
+        // The pipeline entity always keeps the latest pipeline info on the `details.status`
+        this.ciStatus = pipelines[0].details.status;
+      }
+      this.isLoading = false;
     },
-    mounted() {
-      this.service = new CommitPipelineService(this.endpoint);
-      this.initPolling();
+    errorCallback() {
+      this.ciStatus = {
+        text: 'not found',
+        icon: 'status_notfound',
+        group: 'notfound',
+      };
+      this.isLoading = false;
+      Flash(s__('Something went wrong on our end'));
     },
-    methods: {
-      successCallback(res) {
-        const { pipelines } = res.data;
-        if (pipelines.length > 0) {
-          // The pipeline entity always keeps the latest pipeline info on the `details.status`
-          this.ciStatus = pipelines[0].details.status;
-        }
-        this.isLoading = false;
-      },
-      errorCallback() {
-        this.ciStatus = {
-          text: 'not found',
-          icon: 'status_notfound',
-          group: 'notfound',
-        };
-        this.isLoading = false;
-        Flash(s__('Something went wrong on our end'));
-      },
-      initPolling() {
-        this.poll = new Poll({
-          resource: this.service,
-          method: 'fetchData',
-          successCallback: response => this.successCallback(response),
-          errorCallback: this.errorCallback,
-        });
+    initPolling() {
+      this.poll = new Poll({
+        resource: this.service,
+        method: 'fetchData',
+        successCallback: response => this.successCallback(response),
+        errorCallback: this.errorCallback,
+      });
+
+      if (!Visibility.hidden()) {
+        this.isLoading = true;
+        this.poll.makeRequest();
+      } else {
+        this.fetchPipelineCommitData();
+      }
 
+      Visibility.change(() => {
         if (!Visibility.hidden()) {
-          this.isLoading = true;
-          this.poll.makeRequest();
+          this.poll.restart();
         } else {
-          this.fetchPipelineCommitData();
+          this.poll.stop();
         }
-
-        Visibility.change(() => {
-          if (!Visibility.hidden()) {
-            this.poll.restart();
-          } else {
-            this.poll.stop();
-          }
-        });
-      },
-      fetchPipelineCommitData() {
-        this.service.fetchData()
-          .then(this.successCallback)
-          .catch(this.errorCallback);
-      },
+      });
     },
-    destroy() {
-      this.poll.stop();
+    fetchPipelineCommitData() {
+      this.service
+        .fetchData()
+        .then(this.successCallback)
+        .catch(this.errorCallback);
     },
-  };
+  },
+  destroy() {
+    this.poll.stop();
+  },
+};
 </script>
 <template>
-  <div>
+  <div class="ci-status-link">
     <loading-icon
       v-if="isLoading"
       label="Loading pipeline status"
@@ -113,6 +114,7 @@
         :title="statusTitle"
         :aria-label="statusTitle"
         :status="ciStatus"
+        :size="24"
         data-container="body"
       />
     </a>
diff --git a/app/assets/javascripts/reports/store/actions.js b/app/assets/javascripts/reports/store/actions.js
new file mode 100644
index 0000000000000000000000000000000000000000..15c077b0fd8762316bfa0307a0140e6ba4197f60
--- /dev/null
+++ b/app/assets/javascripts/reports/store/actions.js
@@ -0,0 +1,67 @@
+import Visibility from 'visibilityjs';
+import axios from '../../lib/utils/axios_utils';
+import Poll from '../../lib/utils/poll';
+import * as types from './mutation_types';
+
+export const setEndpoint = ({ commit }, endpoint) => commit(types.SET_ENDPOINT, endpoint);
+
+export const requestReports = ({ commit }) => commit(types.REQUEST_REPORTS);
+
+let eTagPoll;
+
+export const clearEtagPoll = () => {
+  eTagPoll = null;
+};
+
+export const stopPolling = () => {
+  if (eTagPoll) eTagPoll.stop();
+};
+
+export const restartPolling = () => {
+  if (eTagPoll) eTagPoll.restart();
+};
+
+/**
+ * We need to poll the reports endpoint while they are being parsed in the Backend.
+ * This can take up to one minute.
+ *
+ * Poll.js will handle etag response.
+ * While http status code is 204, it means it's parsing, and we'll keep polling
+ * When http status code is 200, it means parsing is done, we can show the results & stop polling
+ * When http status code is 500, it means parsing went wrong and we stop polling
+ */
+export const fetchReports = ({ state, dispatch }) => {
+  dispatch('requestReports');
+
+  eTagPoll = new Poll({
+    resource: {
+      getReports(endpoint) {
+        return axios.get(endpoint);
+      },
+    },
+    data: state.endpoint,
+    method: 'getReports',
+    successCallback: ({ data }) => dispatch('receiveReportsSuccess', data),
+    errorCallback: () => dispatch('receiveReportsError'),
+  });
+
+  if (!Visibility.hidden()) {
+    eTagPoll.makeRequest();
+  }
+
+  Visibility.change(() => {
+    if (!Visibility.hidden()) {
+      dispatch('restartPolling');
+    } else {
+      dispatch('stopPolling');
+    }
+  });
+};
+
+export const receiveReportsSuccess = ({ commit }, response) =>
+  commit(types.RECEIVE_REPORTS_SUCCESS, response);
+
+export const receiveReportsError = ({ commit }) => commit(types.RECEIVE_REPORTS_ERROR);
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/reports/store/index.js b/app/assets/javascripts/reports/store/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..af4f9688fb47a9601b843a3b3f25a80aeb26c978
--- /dev/null
+++ b/app/assets/javascripts/reports/store/index.js
@@ -0,0 +1,13 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import * as actions from './actions';
+import mutations from './mutations';
+import state from './state';
+
+Vue.use(Vuex);
+
+export default () => new Vuex.Store({
+  actions,
+  mutations,
+  state: state(),
+});
diff --git a/app/assets/javascripts/reports/store/mutation_types.js b/app/assets/javascripts/reports/store/mutation_types.js
new file mode 100644
index 0000000000000000000000000000000000000000..77722974c45750b06413a34291504edb25e50a67
--- /dev/null
+++ b/app/assets/javascripts/reports/store/mutation_types.js
@@ -0,0 +1,5 @@
+export const SET_ENDPOINT = 'SET_ENDPOINT';
+
+export const REQUEST_REPORTS = 'REQUEST_REPORTS';
+export const RECEIVE_REPORTS_SUCCESS = 'RECEIVE_REPORTS_SUCCESS';
+export const RECEIVE_REPORTS_ERROR = 'RECEIVE_REPORTS_ERROR';
diff --git a/app/assets/javascripts/reports/store/mutations.js b/app/assets/javascripts/reports/store/mutations.js
new file mode 100644
index 0000000000000000000000000000000000000000..d9d301826cf02f775155ecf2be2375e45f253395
--- /dev/null
+++ b/app/assets/javascripts/reports/store/mutations.js
@@ -0,0 +1,26 @@
+/* eslint-disable no-param-reassign */
+import * as types from './mutation_types';
+
+export default {
+  [types.SET_ENDPOINT](state, endpoint) {
+    state.endpoint = endpoint;
+  },
+  [types.REQUEST_REPORTS](state) {
+    state.isLoading = true;
+  },
+  [types.RECEIVE_REPORTS_SUCCESS](state, response) {
+
+    state.isLoading = false;
+
+    state.summary.total = response.summary.total;
+    state.summary.resolved = response.summary.resolved;
+    state.summary.failed = response.summary.failed;
+
+    state.reports = response.suites;
+
+  },
+  [types.RECEIVE_REPORTS_ERROR](state) {
+    state.isLoading = false;
+    state.hasError = true;
+  },
+};
diff --git a/app/assets/javascripts/reports/store/state.js b/app/assets/javascripts/reports/store/state.js
new file mode 100644
index 0000000000000000000000000000000000000000..97f9d0a6859ccd8a02bef6c9ca91fa78428d7532
--- /dev/null
+++ b/app/assets/javascripts/reports/store/state.js
@@ -0,0 +1,28 @@
+export default () => ({
+  endpoint: null,
+
+  isLoading: false,
+  hasError: false,
+
+  summary: {
+    total: 0,
+    resolved: 0,
+    failed: 0,
+  },
+
+  /**
+   * Each report will have the following format:
+   * {
+   *   name: {String},
+   *   summary: {
+   *     total: {Number},
+   *     resolved: {Number},
+   *     failed: {Number},
+   *   },
+   *   new_failures: {Array.<Object>},
+   *   resolved_failures: {Array.<Object>},
+   *   existing_failures: {Array.<Object>},
+   * }
+   */
+  reports: [],
+});
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
index d22a1e1ac66d9b9f22e350adbc06fa9e60855daa..dd155c133cea209dada67db251c86e88200889a8 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
@@ -187,7 +187,7 @@ export default {
       <template v-else-if="hasOneUser">
         <a
           :href="assigneeUrl(firstUser)"
-          class="author_link bold"
+          class="author-link bold"
         >
           <img
             :alt="assigneeAlt(firstUser)"
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index 33dd6c981b6f0dde423c364e5c71d98dceba9c1b..56d57f6aac89d2e52df60fa40140ce121b8e74a2 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -120,7 +120,7 @@
       >
         <a
           :href="participant.web_url"
-          class="author_link"
+          class="author-link"
         >
           <user-avatar-image
             :lazy="true"
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index e3d7645040dc945c811c091fcca8e6b8b02e17a2..e19bbbacf4d7b03c8f1d1b8c08dd4dd53ead49eb 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -206,8 +206,8 @@ function UsersSelect(currentUser, els, options = {}) {
             return $collapsedSidebar.html(collapsedAssigneeTemplate(user));
           });
       };
-      collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <a class="author_link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>');
-      assigneeTemplate = _.template('<% if (username) { %> <a class="author_link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself"> No assignee - <a href="#" class="js-assign-yourself"> assign yourself </a> </span> <% } %>');
+      collapsedAssigneeTemplate = _.template('<% if( avatar ) { %> <a class="author-link" href="/<%- username %>"> <img width="24" class="avatar avatar-inline s24" alt="" src="<%- avatar %>"> </a> <% } else { %> <i class="fa fa-user"></i> <% } %>');
+      assigneeTemplate = _.template('<% if (username) { %> <a class="author-link bold" href="/<%- username %>"> <% if( avatar ) { %> <img width="32" class="avatar avatar-inline s32" alt="" src="<%- avatar %>"> <% } %> <span class="author"><%- name %></span> <span class="username"> @<%- username %> </span> </a> <% } else { %> <span class="no-value assign-yourself"> No assignee - <a href="#" class="js-assign-yourself"> assign yourself </a> </span> <% } %>');
       return $dropdown.glDropdown({
         showMenuAbove: showMenuAbove,
         data: function(term, callback) {
diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
index dc5760bce287de95034e53c76bc28fc795abb9f3..d272bf3f55faca2ab85720ac474530ffcb90e12d 100644
--- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue
+++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
@@ -13,12 +13,19 @@
  * />
  */
 import tooltip from '../directives/tooltip';
+import Icon from '../components/icon.vue';
 
 export default {
   name: 'ClipboardButton',
+
   directives: {
     tooltip,
   },
+
+  components: {
+    Icon,
+  },
+
   props: {
     text: {
       type: String,
@@ -58,10 +65,6 @@ export default {
     type="button"
     class="btn"
   >
-    <i
-      aria-hidden="true"
-      class="fa fa-clipboard"
-    >
-    </i>
+    <icon name="duplicate" />
   </button>
 </template>
diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
index b1c2df54ef661cd378bacc5fcba6fd428b583da9..f44d361c47e29fc3e7c81f450c18be49586a95dc 100644
--- a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
+++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
@@ -1,4 +1,5 @@
 <script>
+import { roundOffFloat } from '~/lib/utils/common_utils';
 import tooltip from '~/vue_shared/directives/tooltip';
 
 export default {
@@ -70,7 +71,7 @@ export default {
   },
   methods: {
     getPercent(count) {
-      return Math.ceil((count / this.totalCount) * 100);
+      return roundOffFloat((count / this.totalCount) * 100, 1);
     },
     barStyle(percent) {
       return `width: ${percent}%;`;
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index c1ec11e434a037405f51bee4e5d1f2ba92f7d26a..94fa799313346d46ab34d8835c900dd07acb3f08 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -7,7 +7,7 @@
 .avatar-circle {
   float: left;
   margin-right: 15px;
-  border-radius: $avatar_radius;
+  border-radius: $avatar-radius;
   border: 1px solid $avatar-border;
   &.s16 { @include avatar-size(16px, 6px); }
   &.s18 { @include avatar-size(18px, 6px); }
@@ -110,7 +110,7 @@
   color: $white-light;
   border: 1px solid $avatar-border;
   border-radius: 1em;
-  font-family: $regular_font;
+  font-family: $regular-font;
   font-size: 9px;
   line-height: 16px;
   text-align: center;
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 523fcb05a874133597f90c885d5fa6b2680f4c54..646cedd79ed471bca30c6bec634a48adf8760de5 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -294,6 +294,10 @@
 .btn-clipboard {
   border: 0;
   padding: 0 5px;
+
+  svg {
+    top: auto;
+  }
 }
 
 .input-group-prepend,
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 218e37602dd994cf9a17bd5a18c2ef06ac42b995..637587de597ad042138934b81c25e9eeb5fb6784 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -113,8 +113,6 @@ hr {
 
 .item-title { font-weight: $gl-font-weight-bold; }
 
-/** FLASH message **/
-.author_link,
 .author-link {
   color: $gl-link-color;
 }
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index a10ff3eecb33819efb9beafcd6240acc8f5efc39..d7149d9362227c3410aba5a7c88064e9e8672d38 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -80,7 +80,7 @@ label {
   .form-control {
     height: 29px;
     background: $white-light;
-    font-family: $monospace_font;
+    font-family: $monospace-font;
   }
 
   .input-group-prepend .btn,
diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss
index 813a1711ea2fb3e60d54dc949f038ff598fd8af2..452e946f95f144a152cf7612d7d20ad87ca07245 100644
--- a/app/assets/stylesheets/framework/highlight.scss
+++ b/app/assets/stylesheets/framework/highlight.scss
@@ -9,8 +9,8 @@
     padding: 10px 0;
     border: 0;
     border-radius: 0;
-    font-family: $monospace_font;
-    font-size: $code_font_size;
+    font-family: $monospace-font;
+    font-size: $code-font-size;
     line-height: 19px;
     margin: 0;
     overflow: auto;
@@ -22,7 +22,7 @@
     code {
       display: inline-block;
       min-width: 100%;
-      font-family: $monospace_font;
+      font-family: $monospace-font;
       white-space: normal;
       word-wrap: normal;
       padding: 0;
@@ -44,7 +44,7 @@
     float: left;
 
     a {
-      font-family: $monospace_font;
+      font-family: $monospace-font;
       display: block;
       font-size: $code_font_size !important;
       min-height: 19px;
diff --git a/app/assets/stylesheets/framework/jquery.scss b/app/assets/stylesheets/framework/jquery.scss
index 300ba4f2de62cd5d5a0a390687a61e98757f6571..d1360a0c0eb97175a49e54f7128290a6d05fa792 100644
--- a/app/assets/stylesheets/framework/jquery.scss
+++ b/app/assets/stylesheets/framework/jquery.scss
@@ -1,5 +1,5 @@
 .ui-widget {
-  font-family: $regular_font;
+  font-family: $regular-font;
   font-size: $font-size-base;
 
   .ui-state-default {
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index d54490c87c68d2e0f02d4085d0897357169005f0..4b67eab05b3bf4777102efcbb1ae9b9f30c50585 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -259,7 +259,7 @@ ul.controls {
       margin-right: 0;
     }
 
-    .author_link {
+    .author-link {
       .avatar-inline {
         margin-left: 0;
         margin-right: 0;
@@ -270,7 +270,7 @@ ul.controls {
 
   .issuable-pipeline-broken a,
   .issuable-pipeline-status a,
-  .author_link {
+  .author-link {
     display: flex;
   }
 }
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 76ebfc22ef760fd053a7f607922a30956e47eaf7..11d332be1beb1447e80a77b1b1c4a2c8bcd4e808 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -3,13 +3,13 @@
  * Mixins with fixed values
  */
 
-@mixin str-truncated($max_width: 82%) {
+@mixin str-truncated($max-width: 82%) {
   display: inline-block;
   overflow: hidden;
   text-overflow: ellipsis;
   vertical-align: top;
   white-space: nowrap;
-  max-width: $max_width;
+  max-width: $max-width;
 }
 
 /*
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 8c7164009132efc04ba8b155853a3df9aa1e786e..c4dbcf2ddc933bc6d1ee9f857e0f1123e4530b8b 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -33,11 +33,11 @@
 
   @include media-breakpoint-up(sm) {
     &:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
-      padding-right: $gutter_collapsed_width;
+      padding-right: $gutter-collapsed-width;
     }
 
     .merge-request-tabs-holder.affix {
-      right: $gutter_collapsed_width;
+      right: $gutter-collapsed-width;
     }
   }
 
@@ -67,21 +67,21 @@
 
   @include media-breakpoint-only(sm) {
     &:not(.wiki-sidebar):not(.build-sidebar):not(.issuable-bulk-update-sidebar) .content-wrapper {
-      padding-right: $gutter_collapsed_width;
+      padding-right: $gutter-collapsed-width;
     }
   }
 
   @include media-breakpoint-up(md) {
     .content-wrapper {
-      padding-right: $gutter_width;
+      padding-right: $gutter-width;
     }
 
     &:not(.with-overlay) .merge-request-tabs-holder.affix {
-      right: $gutter_width;
+      right: $gutter-width;
     }
 
     &.with-overlay .merge-request-tabs-holder.affix {
-      right: $gutter_collapsed_width;
+      right: $gutter-collapsed-width;
     }
   }
 }
diff --git a/app/assets/stylesheets/framework/stacked_progress_bar.scss b/app/assets/stylesheets/framework/stacked_progress_bar.scss
index 528ba53a48b293f0c156475cab1f2f595efaa9fa..29a2d5881f77e6fac579cbff445482ab57d3c74a 100644
--- a/app/assets/stylesheets/framework/stacked_progress_bar.scss
+++ b/app/assets/stylesheets/framework/stacked_progress_bar.scss
@@ -10,7 +10,7 @@
   .status-neutral,
   .status-red, {
     height: 100%;
-    min-width: 30px;
+    min-width: 40px;
     padding: 0 5px;
     font-size: $tooltip-font-size;
     font-weight: normal;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 9874c92860432ab96e80e98beed5f5fdeb287d57..56307777a72955cd28062be1f9d7c26856cf6d69 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -44,7 +44,7 @@
 
   // Single code lines should wrap
   code {
-    font-family: $monospace_font;
+    font-family: $monospace-font;
     white-space: pre-wrap;
     word-wrap: normal;
   }
@@ -321,7 +321,7 @@ h6 {
 
 /** CODE **/
 pre {
-  font-family: $monospace_font;
+  font-family: $monospace-font;
   display: block;
   padding: $gl-padding-8;
   margin: 0 0 $gl-padding-8;
@@ -342,7 +342,7 @@ code {
 }
 
 .monospace {
-  font-family: $monospace_font;
+  font-family: $monospace-font;
 }
 
 .weight-normal {
@@ -381,7 +381,7 @@ code {
  *
  */
 textarea.js-gfm-input {
-  font-family: $monospace_font;
+  font-family: $monospace-font;
   font-size: 13px;
 }
 
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 6c2fdbe0608eadcb6d9bf35592233de9243b1892..efc54196b75f72238bc45089ed5f511fe2112254 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -2,9 +2,9 @@
  * Layout
  */
 $grid-size: 8px;
-$gutter_collapsed_width: 62px;
-$gutter_width: 290px;
-$gutter_inner_width: 250px;
+$gutter-collapsed-width: 62px;
+$gutter-width: 290px;
+$gutter-inner-width: 250px;
 $sidebar-transition-duration: 0.3s;
 $sidebar-breakpoint: 1024px;
 $default-transition-duration: 0.15s;
@@ -233,8 +233,8 @@ $md-area-border: #ddd;
 /*
  * Code
  */
-$code_font_size: 90%;
-$code_line_height: 1.6;
+$code-font-size: 90%;
+$code-line-height: 1.6;
 
 /*
  * Tooltips
@@ -371,9 +371,9 @@ $diff-jagged-border-gradient-color: darken($white-normal, 8%);
 /*
  * Fonts
  */
-$monospace_font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono',
+$monospace-font: 'Menlo', 'DejaVu Sans Mono', 'Liberation Mono', 'Consolas', 'Ubuntu Mono',
   'Courier New', 'andale mono', 'lucida console', monospace;
-$regular_font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
+$regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
   'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
 
 /*
@@ -526,7 +526,7 @@ $issue-board-list-difference-md: $issue-board-list-difference-sm + $issue-boards
 /*
 * Avatar
 */
-$avatar_radius: 50%;
+$avatar-radius: 50%;
 $avatar-border: $gray-normal;
 $avatar-border-hover: $gray-darker;
 $avatar-background: $gray-lightest;
@@ -830,8 +830,8 @@ $secondary: $gray-light;
 $input-disabled-bg: $gray-light;
 $input-border-color: $theme-gray-200;
 $input-color: $gl-text-color;
-$font-family-sans-serif: $regular_font;
-$font-family-monospace: $monospace_font;
+$font-family-sans-serif: $regular-font;
+$font-family-monospace: $monospace-font;
 $input-line-height: 20px;
 $btn-line-height: 20px;
 $table-accent-bg: $gray-light;
diff --git a/app/assets/stylesheets/mailers/highlighted_diff_email.scss b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
index 1835c4364d3f3cc8312ebc7f2667a2ac8b09fbd4..8b234a5a65699da6ed0b9f8f1e1de810e823926e 100644
--- a/app/assets/stylesheets/mailers/highlighted_diff_email.scss
+++ b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
@@ -77,13 +77,13 @@ $highlighted-gc-bg: #eaf2f5;
 .code {
   background-color: $white-light;
   font-family: monospace;
-  font-size: $code_font_size;
+  font-size: $code-font-size;
   -premailer-cellpadding: 0;
   -premailer-cellspacing: 0;
   -premailer-width: 100%;
 
   > tr {
-    line-height: $code_line_height;
+    line-height: $code-line-height;
   }
 }
 
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 5de53892fac5598f7bfc29f38e116d7e396ef450..7347da2ae6160f2c7f5262f97fb8c6727160812f 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -63,7 +63,7 @@
     width: 100%;
 
     &.is-compact {
-      width: calc(100% - #{$gutter_width});
+      width: calc(100% - #{$gutter-width});
     }
   }
 }
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index f75be4e01cd380a03b552e55ed11ca4bb700f2c0..9b51c54a0fcb8b50261611bc7af7f3b564f01b56 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -79,7 +79,7 @@
 .commit-message-container {
   background-color: $body-bg;
   position: relative;
-  font-family: $monospace_font;
+  font-family: $monospace-font;
   $left: 12px;
   overflow: hidden; // See https://gitlab.com/gitlab-org/gitlab-ce/issues/13987
   .max-width-marker {
@@ -205,7 +205,7 @@
   > .ci-status-link,
   > .btn,
   > .commit-sha-group {
-    margin-left: $gl-padding-8;
+    margin-left: $gl-padding;
   }
 }
 
@@ -235,10 +235,6 @@
     fill: $gl-text-color-secondary;
   }
 
-  .fa-clipboard {
-    color: $gl-text-color-secondary;
-  }
-
   :first-child {
     border-bottom-left-radius: $border-radius-default;
     border-top-left-radius: $border-radius-default;
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index a22c666a525478de952bafe19f7a93875132f8db..e2c0a7a622587eb745db38308a04647b0413f160 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -368,7 +368,7 @@
 
       .fa {
         color: $gl-text-color-secondary;
-        font-size: $code_font_size;
+        font-size: $code-font-size;
       }
     }
   }
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index 2e007c525924997503c6b655e2f699cd93add7b7..37ed5ae674a484e53e6c5f8d6983491ef5760045 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -10,7 +10,7 @@
   }
 
   .issue_created_ago,
-  .author_link {
+  .author-link {
     white-space: nowrap;
   }
 
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 5e39bbb9890c15e8a26851a465ede5a9b5309337..b616357bb8d0b03f750f56e610d617d1f7b49730 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -56,7 +56,7 @@
 
     table {
       width: 100%;
-      font-family: $monospace_font;
+      font-family: $monospace-font;
       border: 0;
       border-collapse: separate;
       margin: 0;
@@ -73,8 +73,8 @@
       }
 
       .line_holder td {
-        line-height: $code_line_height;
-        font-size: $code_font_size;
+        line-height: $code-line-height;
+        font-size: $code-font-size;
 
         &.noteable_line {
           position: relative;
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 437621299e037b46e51b9c26ca580a8d7bb6e5fd..ddd1f8cc98a50a3af9668a3f48e03377bd95eecb 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -84,7 +84,7 @@
   .soft-wrap-toggle {
     display: inline-block;
     vertical-align: top;
-    font-family: $regular_font;
+    font-family: $regular-font;
   }
 
   .soft-wrap-toggle {
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 8915b323b3c012594a8b6c770d3c3017dbbf0357..8a07401734490057c6db72fbecb0feb3e4d46b05 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -478,7 +478,7 @@
     }
 
     .deploy-info-text-link {
-      font-family: $monospace_font;
+      font-family: $monospace-font;
       fill: $gl-link-color;
 
       &:hover {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index f9fd9f1ab8b4523c3d38b3fc42308c9c39413f2c..797b106de23b3dde7bf9dd150479a36fd1cf842b 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -166,7 +166,7 @@
     border-bottom: 1px solid $border-gray-normal;
     // This prevents the mess when resizing the sidebar
     // of elements repositioning themselves..
-    width: $gutter_inner_width;
+    width: $gutter-inner-width;
     // --
 
     &.issuable-sidebar-header {
@@ -197,7 +197,7 @@
     }
 
     &.assignee {
-      .author_link {
+      .author-link {
         display: block;
         padding-left: 42px;
         position: relative;
@@ -290,7 +290,7 @@
   }
 
   &.right-sidebar-expanded {
-    width: $gutter_width;
+    width: $gutter-width;
 
     .value {
       line-height: 1;
@@ -377,11 +377,11 @@
       display: block;
     }
 
-    width: $gutter_collapsed_width;
+    width: $gutter-collapsed-width;
     padding: 0;
 
     .block {
-      width: $gutter_collapsed_width - 2px;
+      width: $gutter-collapsed-width - 2px;
       padding: 15px 0 0;
       border-bottom: 0;
       overflow: hidden;
@@ -486,7 +486,7 @@
       padding-bottom: 0;
       margin-bottom: 10px;
 
-      .author_link {
+      .author-link {
         padding-left: 0;
 
         .avatar {
@@ -595,7 +595,7 @@
   margin: 16px 0 0;
   font-size: 85%;
 
-  .author_link {
+  .author-link {
     color: $gray-darkest;
   }
 }
@@ -620,7 +620,7 @@
     padding-right: 0;
   }
 
-  .author_link {
+  .author-link {
     display: block;
   }
 
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 19fb99bfa93962b5970d60abf3d3c4a72c74e2d4..212e5979273801b053a8d18b3ecb1b5b8107dc84 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -12,7 +12,7 @@
     }
 
     .issuable-meta {
-      .author_link {
+      .author-link {
         display: inline-block;
       }
 
diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss
index e76525fdbf62852bef2a71e9cb41fa409ec17098..d26659701e19e05066af25c7ab481a13031aaee2 100644
--- a/app/assets/stylesheets/pages/merge_conflicts.scss
+++ b/app/assets/stylesheets/pages/merge_conflicts.scss
@@ -1,167 +1,162 @@
 // Disabled to use the color map for creating color schemes
 // scss-lint:disable ColorVariable
 $colors: (
-  white_header_head_neutral   : #e1fad7,
-  white_line_head_neutral     : #effdec,
-  white_button_head_neutral   : #9adb84,
+  white-header-head-neutral   : #e1fad7,
+  white-line-head-neutral     : #effdec,
+  white-button-head-neutral   : #9adb84,
 
-  white_header_head_chosen    : #baf0a8,
-  white_line_head_chosen      : #e1fad7,
-  white_button_head_chosen    : #52c22d,
+  white-header-head-chosen    : #baf0a8,
+  white-line-head-chosen      : #e1fad7,
+  white-button-head-chosen    : #52c22d,
 
-  white_header_origin_neutral : #e0f0ff,
-  white_line_origin_neutral   : #f2f9ff,
-  white_button_origin_neutral : #87c2fa,
+  white-header-origin-neutral : #e0f0ff,
+  white-line-origin-neutral   : #f2f9ff,
+  white-button-origin-neutral : #87c2fa,
 
-  white_header_origin_chosen  : #add8ff,
-  white_line_origin_chosen    : #e0f0ff,
-  white_button_origin_chosen  : #268ced,
+  white-header-origin-chosen  : #add8ff,
+  white-line-origin-chosen    : #e0f0ff,
+  white-button-origin-chosen  : #268ced,
 
-  white_header_not_chosen     : #f0f0f0,
-  white_line_not_chosen       : $gray-light,
+  white-header-not-chosen     : #f0f0f0,
+  white-line-not-chosen       : $gray-light,
 
+  dark-header-head-neutral   : rgba(#3f3, .2),
+  dark-line-head-neutral     : rgba(#3f3, .1),
+  dark-button-head-neutral   : #40874f,
 
-  dark_header_head_neutral   : rgba(#3f3, .2),
-  dark_line_head_neutral     : rgba(#3f3, .1),
-  dark_button_head_neutral   : #40874f,
+  dark-header-head-chosen    : rgba(#3f3, .33),
+  dark-line-head-chosen      : rgba(#3f3, .2),
+  dark-button-head-chosen    : #258537,
 
-  dark_header_head_chosen    : rgba(#3f3, .33),
-  dark_line_head_chosen      : rgba(#3f3, .2),
-  dark_button_head_chosen    : #258537,
+  dark-header-origin-neutral : rgba(#2878c9, .4),
+  dark-line-origin-neutral   : rgba(#2878c9, .3),
+  dark-button-origin-neutral : #2a5c8c,
 
-  dark_header_origin_neutral : rgba(#2878c9, .4),
-  dark_line_origin_neutral   : rgba(#2878c9, .3),
-  dark_button_origin_neutral : #2a5c8c,
+  dark-header-origin-chosen  : rgba(#2878c9, .6),
+  dark-line-origin-chosen    : rgba(#2878c9, .4),
+  dark-button-origin-chosen  : #1d6cbf,
 
-  dark_header_origin_chosen  : rgba(#2878c9, .6),
-  dark_line_origin_chosen    : rgba(#2878c9, .4),
-  dark_button_origin_chosen  : #1d6cbf,
+  dark-header-not-chosen     : rgba(#fff, .25),
+  dark-line-not-chosen       : rgba(#fff, .1),
 
-  dark_header_not_chosen     : rgba(#fff, .25),
-  dark_line_not_chosen       : rgba(#fff, .1),
+  monokai-header-head-neutral   : rgba(#a6e22e, .25),
+  monokai-line-head-neutral     : rgba(#a6e22e, .1),
+  monokai-button-head-neutral   : #376b20,
 
+  monokai-header-head-chosen    : rgba(#a6e22e, .4),
+  monokai-line-head-chosen      : rgba(#a6e22e, .25),
+  monokai-button-head-chosen    : #39800d,
 
-  monokai_header_head_neutral   : rgba(#a6e22e, .25),
-  monokai_line_head_neutral     : rgba(#a6e22e, .1),
-  monokai_button_head_neutral   : #376b20,
+  monokai-header-origin-neutral : rgba(#60d9f1, .35),
+  monokai-line-origin-neutral   : rgba(#60d9f1, .15),
+  monokai-button-origin-neutral : #38848c,
 
-  monokai_header_head_chosen    : rgba(#a6e22e, .4),
-  monokai_line_head_chosen      : rgba(#a6e22e, .25),
-  monokai_button_head_chosen    : #39800d,
+  monokai-header-origin-chosen  : rgba(#60d9f1, .5),
+  monokai-line-origin-chosen    : rgba(#60d9f1, .35),
+  monokai-button-origin-chosen  : #3ea4b2,
 
-  monokai_header_origin_neutral : rgba(#60d9f1, .35),
-  monokai_line_origin_neutral   : rgba(#60d9f1, .15),
-  monokai_button_origin_neutral : #38848c,
+  monokai-header-not-chosen     : rgba(#76715d, .24),
+  monokai-line-not-chosen       : rgba(#76715d, .1),
 
-  monokai_header_origin_chosen  : rgba(#60d9f1, .5),
-  monokai_line_origin_chosen    : rgba(#60d9f1, .35),
-  monokai_button_origin_chosen  : #3ea4b2,
+  solarized-light-header-head-neutral   : rgba(#859900, .37),
+  solarized-light-line-head-neutral     : rgba(#859900, .2),
+  solarized-light-button-head-neutral   : #afb262,
 
-  monokai_header_not_chosen     : rgba(#76715d, .24),
-  monokai_line_not_chosen       : rgba(#76715d, .1),
+  solarized-light-header-head-chosen    : rgba(#859900, .5),
+  solarized-light-line-head-chosen      : rgba(#859900, .37),
+  solarized-light-button-head-chosen    : #94993d,
 
+  solarized-light-header-origin-neutral : rgba(#2878c9, .37),
+  solarized-light-line-origin-neutral   : rgba(#2878c9, .15),
+  solarized-light-button-origin-neutral : #60a1bf,
 
-  solarized_light_header_head_neutral   : rgba(#859900, .37),
-  solarized_light_line_head_neutral     : rgba(#859900, .2),
-  solarized_light_button_head_neutral   : #afb262,
+  solarized-light-header-origin-chosen  : rgba(#2878c9, .6),
+  solarized-light-line-origin-chosen    : rgba(#2878c9, .37),
+  solarized-light-button-origin-chosen  : #2482b2,
 
-  solarized_light_header_head_chosen    : rgba(#859900, .5),
-  solarized_light_line_head_chosen      : rgba(#859900, .37),
-  solarized_light_button_head_chosen    : #94993d,
+  solarized-light-header-not-chosen     : rgba(#839496, .37),
+  solarized-light-line-not-chosen       : rgba(#839496, .2),
 
-  solarized_light_header_origin_neutral : rgba(#2878c9, .37),
-  solarized_light_line_origin_neutral   : rgba(#2878c9, .15),
-  solarized_light_button_origin_neutral : #60a1bf,
+  solarized-dark-header-head-neutral   : rgba(#859900, .35),
+  solarized-dark-line-head-neutral     : rgba(#859900, .15),
+  solarized-dark-button-head-neutral   : #376b20,
 
-  solarized_light_header_origin_chosen  : rgba(#2878c9, .6),
-  solarized_light_line_origin_chosen    : rgba(#2878c9, .37),
-  solarized_light_button_origin_chosen  : #2482b2,
+  solarized-dark-header-head-chosen    : rgba(#859900, .5),
+  solarized-dark-line-head-chosen      : rgba(#859900, .35),
+  solarized-dark-button-head-chosen    : #39800d,
 
-  solarized_light_header_not_chosen     : rgba(#839496, .37),
-  solarized_light_line_not_chosen       : rgba(#839496, .2),
+  solarized-dark-header-origin-neutral : rgba(#2878c9, .35),
+  solarized-dark-line-origin-neutral   : rgba(#2878c9, .15),
+  solarized-dark-button-origin-neutral : #086799,
 
+  solarized-dark-header-origin-chosen  : rgba(#2878c9, .6),
+  solarized-dark-line-origin-chosen    : rgba(#2878c9, .35),
+  solarized-dark-button-origin-chosen  : #0082cc,
 
-  solarized_dark_header_head_neutral   : rgba(#859900, .35),
-  solarized_dark_line_head_neutral     : rgba(#859900, .15),
-  solarized_dark_button_head_neutral   : #376b20,
-
-  solarized_dark_header_head_chosen    : rgba(#859900, .5),
-  solarized_dark_line_head_chosen      : rgba(#859900, .35),
-  solarized_dark_button_head_chosen    : #39800d,
-
-  solarized_dark_header_origin_neutral : rgba(#2878c9, .35),
-  solarized_dark_line_origin_neutral   : rgba(#2878c9, .15),
-  solarized_dark_button_origin_neutral : #086799,
-
-  solarized_dark_header_origin_chosen  : rgba(#2878c9, .6),
-  solarized_dark_line_origin_chosen    : rgba(#2878c9, .35),
-  solarized_dark_button_origin_chosen  : #0082cc,
-
-  solarized_dark_header_not_chosen     : rgba(#839496, .25),
-  solarized_dark_line_not_chosen       : rgba(#839496, .15)
+  solarized-dark-header-not-chosen     : rgba(#839496, .25),
+  solarized-dark-line-not-chosen       : rgba(#839496, .15)
 );
 // scss-lint:enable ColorVariable
 
-
 @mixin color-scheme($color) {
   .header.line_content,
   .diff-line-num {
     &.origin {
-      background-color: map-get($colors, #{$color}_header_origin_neutral);
-      border-color: map-get($colors, #{$color}_header_origin_neutral);
+      background-color: map-get($colors, #{$color}-header-origin-neutral);
+      border-color: map-get($colors, #{$color}-header-origin-neutral);
 
       button {
-        background-color: map-get($colors, #{$color}_button_origin_neutral);
-        border-color: darken(map-get($colors, #{$color}_button_origin_neutral), 15);
+        background-color: map-get($colors, #{$color}-button-origin-neutral);
+        border-color: darken(map-get($colors, #{$color}-button-origin-neutral), 15);
       }
 
       &.selected {
-        background-color: map-get($colors, #{$color}_header_origin_chosen);
-        border-color: map-get($colors, #{$color}_header_origin_chosen);
+        background-color: map-get($colors, #{$color}-header-origin-chosen);
+        border-color: map-get($colors, #{$color}-header-origin-chosen);
 
         button {
-          background-color: map-get($colors, #{$color}_button_origin_chosen);
-          border-color: darken(map-get($colors, #{$color}_button_origin_chosen), 15);
+          background-color: map-get($colors, #{$color}-button-origin-chosen);
+          border-color: darken(map-get($colors, #{$color}-button-origin-chosen), 15);
         }
       }
 
       &.unselected {
-        background-color: map-get($colors, #{$color}_header_not_chosen);
-        border-color: map-get($colors, #{$color}_header_not_chosen);
+        background-color: map-get($colors, #{$color}-header-not-chosen);
+        border-color: map-get($colors, #{$color}-header-not-chosen);
 
         button {
-          background-color: lighten(map-get($colors, #{$color}_button_origin_neutral), 15);
-          border-color: map-get($colors, #{$color}_button_origin_neutral);
+          background-color: lighten(map-get($colors, #{$color}-button-origin-neutral), 15);
+          border-color: map-get($colors, #{$color}-button-origin-neutral);
         }
       }
     }
 
     &.head {
-      background-color: map-get($colors, #{$color}_header_head_neutral);
-      border-color: map-get($colors, #{$color}_header_head_neutral);
+      background-color: map-get($colors, #{$color}-header-head-neutral);
+      border-color: map-get($colors, #{$color}-header-head-neutral);
 
       button {
-        background-color: map-get($colors, #{$color}_button_head_neutral);
-        border-color: darken(map-get($colors, #{$color}_button_head_neutral), 15);
+        background-color: map-get($colors, #{$color}-button-head-neutral);
+        border-color: darken(map-get($colors, #{$color}-button-head-neutral), 15);
       }
 
       &.selected {
-        background-color: map-get($colors, #{$color}_header_head_chosen);
-        border-color: map-get($colors, #{$color}_header_head_chosen);
+        background-color: map-get($colors, #{$color}-header-head-chosen);
+        border-color: map-get($colors, #{$color}-header-head-chosen);
 
         button {
-          background-color: map-get($colors, #{$color}_button_head_chosen);
-          border-color: darken(map-get($colors, #{$color}_button_head_chosen), 15);
+          background-color: map-get($colors, #{$color}-button-head-chosen);
+          border-color: darken(map-get($colors, #{$color}-button-head-chosen), 15);
         }
       }
 
       &.unselected {
-        background-color: map-get($colors, #{$color}_header_not_chosen);
-        border-color: map-get($colors, #{$color}_header_not_chosen);
+        background-color: map-get($colors, #{$color}-header-not-chosen);
+        border-color: map-get($colors, #{$color}-header-not-chosen);
 
         button {
-          background-color: lighten(map-get($colors, #{$color}_button_head_neutral), 15);
-          border-color: map-get($colors, #{$color}_button_head_neutral);
+          background-color: lighten(map-get($colors, #{$color}-button-head-neutral), 15);
+          border-color: map-get($colors, #{$color}-button-head-neutral);
         }
       }
     }
@@ -169,32 +164,31 @@ $colors: (
 
   .line_content {
     &.origin {
-      background-color: map-get($colors, #{$color}_line_origin_neutral);
+      background-color: map-get($colors, #{$color}-line-origin-neutral);
 
       &.selected {
-        background-color: map-get($colors, #{$color}_line_origin_chosen);
+        background-color: map-get($colors, #{$color}-line-origin-chosen);
       }
 
       &.unselected {
-        background-color: map-get($colors, #{$color}_line_not_chosen);
+        background-color: map-get($colors, #{$color}-line-not-chosen);
       }
     }
 
     &.head {
-      background-color: map-get($colors, #{$color}_line_head_neutral);
+      background-color: map-get($colors, #{$color}-line-head-neutral);
 
       &.selected {
-        background-color: map-get($colors, #{$color}_line_head_chosen);
+        background-color: map-get($colors, #{$color}-line-head-chosen);
       }
 
       &.unselected {
-        background-color: map-get($colors, #{$color}_line_not_chosen);
+        background-color: map-get($colors, #{$color}-line-not-chosen);
       }
     }
   }
 }
 
-
 #conflicts {
 
   .white {
@@ -210,11 +204,11 @@ $colors: (
   }
 
   .solarized-light {
-    @include color-scheme('solarized_light')
+    @include color-scheme('solarized-light')
   }
 
   .solarized-dark {
-    @include color-scheme('solarized_dark')
+    @include color-scheme('solarized-dark')
   }
 
   .diff-wrap-lines .line_content {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index c8349a4ef79dea305f97c3594951792bf6bccb5e..7bd0f0bf1e0bd9349e99979f7cbccc6a879437a3 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -208,7 +208,7 @@
         position: absolute;
         content: '...';
         right: 0;
-        font-family: $regular_font;
+        font-family: $regular-font;
         background-color: $gray-light;
       }
     }
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 5e5696b16023f0ea7eec7b0b942cb0529232cd80..dcf590e7331969432d3474843a8c28031a5fd7c4 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -42,7 +42,7 @@
   display: block;
   padding: 10px 0;
   color: $gl-text-color;
-  font-family: $regular_font;
+  font-family: $regular-font;
   border: 0;
 
   &:focus {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 32d140490676638649b8625f5a30da834b07aa92..7fc2936c5e6773de80f925b3c3de664a0f6654b3 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -328,7 +328,7 @@ ul.notes {
   }
 
   .notes_holder {
-    font-family: $regular_font;
+    font-family: $regular-font;
 
     td {
       border: 1px solid $white-normal;
@@ -403,7 +403,7 @@ ul.notes {
     }
   }
 
-  .author_link {
+  .author-link {
     color: $gl-text-color;
   }
 }
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 8b1227b9131b4c3510f66370777b35fc9e75dd22..2d76f0ce0049dba73175863a05725a34ebb95200 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -961,7 +961,7 @@
   overflow: hidden;
 
   .note-textarea {
-    font-family: $monospace_font;
+    font-family: $monospace-font;
   }
 }
 
diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss
index 0d6b0735f700d876d12de1b6b9d90308d2e865d2..64110f9c3a0d546d92c1d74bd34d43eb17e32d48 100644
--- a/app/assets/stylesheets/snippets.scss
+++ b/app/assets/stylesheets/snippets.scss
@@ -6,9 +6,9 @@
 
   $border-style: 1px solid $border-color;
 
-  font-family: $regular_font;
+  font-family: $regular-font;
   font-size: $gl-font-size;
-  line-height: $code_line_height;
+  line-height: $code-line-height;
   color: $gl-text-color;
   margin: 20px;
   font-weight: 200;
@@ -48,9 +48,9 @@
         padding: 10px;
         border: 0;
         border-radius: 0;
-        font-family: $monospace_font;
-        font-size: $code_font_size;
-        line-height: $code_line_height;
+        font-family: $monospace-font;
+        font-size: $code-font-size;
+        line-height: $code-line-height;
         margin: 0;
         overflow: auto;
         overflow-y: hidden;
@@ -66,10 +66,10 @@
       float: left;
 
       .diff-line-num {
-        font-family: $monospace_font;
+        font-family: $monospace-font;
         display: block;
-        font-size: $code_font_size;
-        min-height: $code_line_height;
+        font-size: $code-font-size;
+        min-height: $code-line-height;
         white-space: nowrap;
         color: $black-transparent;
         min-width: 30px;
diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb
index 79ee5b2f91ec5974ba944088663867058da7bcdb..4584ff782a370d5cd3c73973a241aa644cbabfb6 100644
--- a/app/controllers/concerns/lfs_request.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -71,7 +71,22 @@ module LfsRequest
   def lfs_download_access?
     return false unless project.lfs_enabled?
 
-    ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
+    ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code? || deploy_token_can_download_code?
+  end
+
+  def deploy_token_can_download_code?
+    deploy_token_present? &&
+      deploy_token.project == project &&
+      deploy_token.active? &&
+      deploy_token.read_repository?
+  end
+
+  def deploy_token_present?
+    user && user.is_a?(DeployToken)
+  end
+
+  def deploy_token
+    user
   end
 
   def lfs_upload_access?
@@ -86,7 +101,7 @@ module LfsRequest
   end
 
   def user_can_download_code?
-    has_authentication_ability?(:download_code) && can?(user, :download_code, project)
+    has_authentication_ability?(:download_code) && can?(user, :download_code, project) && !deploy_token_present?
   end
 
   def build_can_download_code?
diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb
index fccbdbca0f61912fb5cd7c845738c5370964e702..53f70446d95dd946c73c89beef99b53bbc642dd5 100644
--- a/app/controllers/import/gitlab_controller.rb
+++ b/app/controllers/import/gitlab_controller.rb
@@ -1,4 +1,7 @@
 class Import::GitlabController < Import::BaseController
+  MAX_PROJECT_PAGES = 15
+  PER_PAGE_PROJECTS = 100
+
   before_action :verify_gitlab_import_enabled
   before_action :gitlab_auth, except: :callback
 
@@ -10,7 +13,7 @@ class Import::GitlabController < Import::BaseController
   end
 
   def status
-    @repos = client.projects
+    @repos = client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS)
 
     @already_added_projects = find_already_added_projects('gitlab')
     already_added_projects_names = @already_added_projects.pluck(:import_source)
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 074db361949391b00ec06b9e72d91b56f9332f47..56a7b766b77a1cc0b5a99fe33101931e44e7e6f8 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -99,7 +99,8 @@ class ProfilesController < Profiles::ApplicationController
       :username,
       :website_url,
       :organization,
-      :preferred_language
+      :preferred_language,
+      :private_profile
     )
   end
 end
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 9dc0c31be49a9fd270ea1773005eb88fe77cdcee..b7c656246ef1db3b478477897e0237f0ee8801e3 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -112,7 +112,7 @@ class Projects::WikisController < Projects::ApplicationController
   private
 
   def load_project_wiki
-    @project_wiki = ProjectWiki.new(@project, current_user)
+    @project_wiki = load_wiki
 
     # Call #wiki to make sure the Wiki Repo is initialized
     @project_wiki.wiki
@@ -128,6 +128,10 @@ class Projects::WikisController < Projects::ApplicationController
     false
   end
 
+  def load_wiki
+    ProjectWiki.new(@project, current_user)
+  end
+
   def wiki_params
     params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha)
   end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 9dd652206fe8a5c8848ee6ba0731a6959aa26527..4ca42e2d4a20e0f63d15355aaa588b22d72c1348 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -157,6 +157,8 @@ class SessionsController < Devise::SessionsController
   end
 
   def auto_sign_in_with_provider
+    return unless Gitlab::Auth.omniauth_enabled?
+
     provider = Gitlab.config.omniauth.auto_sign_in_with_provider
     return unless provider.present?
 
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 31f47a7aa7c21a3bc12b3d0b1d7a410d24919cc8..2f65f4a7403426ab5f60f3926a194cb829ff9969 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -13,6 +13,8 @@ class UsersController < ApplicationController
 
   skip_before_action :authenticate_user!
   before_action :user, except: [:exists]
+  before_action :authorize_read_user_profile!,
+                only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :snippets]
 
   def show
     respond_to do |format|
@@ -148,4 +150,8 @@ class UsersController < ApplicationController
   def build_canonical_path(user)
     url_for(safe_params.merge(username: user.to_param))
   end
+
+  def authorize_read_user_profile!
+    access_denied! unless can?(current_user, :read_user_profile, user)
+  end
 end
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index 0754123a3cfbfd1f54ec79dea7a86857ed33d185..0eeba1d2428a656018a9d44063a6438776e89d6f 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -8,6 +8,7 @@
 #     owned: boolean
 #     parent: Group
 #     all_available: boolean (defaults to true)
+#     min_access_level: integer
 #
 # Users with full private access can see all groups. The `owned` and `parent`
 # params can be used to restrict the groups that are returned.
@@ -39,6 +40,7 @@ class GroupsFinder < UnionFinder
 
   def all_groups
     return [owned_groups] if params[:owned]
+    return [groups_with_min_access_level] if min_access_level?
     return [Group.all] if current_user&.full_private_access? && all_available?
 
     groups = []
@@ -56,6 +58,16 @@ class GroupsFinder < UnionFinder
     current_user.groups
   end
 
+  def groups_with_min_access_level
+    groups = current_user
+      .groups
+      .where('members.access_level >= ?', params[:min_access_level])
+
+    Gitlab::GroupHierarchy
+      .new(groups)
+      .base_and_descendants
+  end
+
   def by_parent(groups)
     return groups unless params[:parent]
 
@@ -73,4 +85,8 @@ class GroupsFinder < UnionFinder
   def all_available?
     params.fetch(:all_available, true)
   end
+
+  def min_access_level?
+    current_user && params[:min_access_level].present?
+  end
 end
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
index 5aea0cb81928a061c2cfd53c6ec703d73ca9d229..a56a3a1e1a9bdb1ed928fc4e495bb8ba8e00b33c 100644
--- a/app/finders/personal_projects_finder.rb
+++ b/app/finders/personal_projects_finder.rb
@@ -1,6 +1,9 @@
 class PersonalProjectsFinder < UnionFinder
-  def initialize(user)
+  include Gitlab::Allowable
+
+  def initialize(user, params = {})
     @user = user
+    @params = params
   end
 
   # Finds the projects belonging to the user in "@user", limited to either
@@ -8,9 +11,13 @@ class PersonalProjectsFinder < UnionFinder
   #
   # current_user - When given the list of projects is limited to those only
   #                visible by this user.
+  # params       - Optional query parameters
+  #                  min_access_level: integer
   #
   # Returns an ActiveRecord::Relation.
   def execute(current_user = nil)
+    return Project.none unless can?(current_user, :read_user_profile, @user)
+
     segments = all_projects(current_user)
 
     find_union(segments, Project).includes(:namespace).order_updated_desc
@@ -19,11 +26,21 @@ class PersonalProjectsFinder < UnionFinder
   private
 
   def all_projects(current_user)
-    projects = []
+    return [projects_with_min_access_level(current_user)] if current_user && min_access_level?
 
+    projects = []
     projects << @user.personal_projects.visible_to_user(current_user) if current_user
     projects << @user.personal_projects.public_to_user(current_user)
-
     projects
   end
+
+  def projects_with_min_access_level(current_user)
+    @user
+      .personal_projects
+      .visible_to_user_and_access_level(current_user, @params[:min_access_level])
+  end
+
+  def min_access_level?
+    @params[:min_access_level].present?
+  end
 end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index b06595081e7f8b5f2e7f8e083a3832273caf21d6..cac6643eff3a71f4f6098107c1e877767d6ac412 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -17,6 +17,7 @@
 #     search: string
 #     non_archived: boolean
 #     archived: 'only' or boolean
+#     min_access_level: integer
 #
 class ProjectsFinder < UnionFinder
   include CustomAttributesFilter
@@ -34,7 +35,7 @@ class ProjectsFinder < UnionFinder
     user = params.delete(:user)
     collection =
       if user
-        PersonalProjectsFinder.new(user).execute(current_user)
+        PersonalProjectsFinder.new(user, finder_params).execute(current_user)
       else
         init_collection
       end
@@ -65,6 +66,8 @@ class ProjectsFinder < UnionFinder
   def collection_with_user
     if owned_projects?
       current_user.owned_projects
+    elsif min_access_level?
+      current_user.authorized_projects.where('project_authorizations.access_level >= ?', params[:min_access_level])
     else
       if private_only?
         current_user.authorized_projects
@@ -76,7 +79,7 @@ class ProjectsFinder < UnionFinder
 
   # Builds a collection for an anonymous user.
   def collection_without_user
-    if private_only? || owned_projects?
+    if private_only? || owned_projects? || min_access_level?
       Project.none
     else
       Project.public_to_user
@@ -91,6 +94,10 @@ class ProjectsFinder < UnionFinder
     params[:non_public].present?
   end
 
+  def min_access_level?
+    params[:min_access_level].present?
+  end
+
   def by_ids(items)
     project_ids_relation ? items.where(id: project_ids_relation) : items
   end
@@ -143,4 +150,10 @@ class ProjectsFinder < UnionFinder
       projects
     end
   end
+
+  def finder_params
+    return {} unless min_access_level?
+
+    { min_access_level: params[:min_access_level] }
+  end
 end
diff --git a/app/finders/user_recent_events_finder.rb b/app/finders/user_recent_events_finder.rb
index 74776b2ed1f1cfa1f3880476150e90e0ea82d0fa..876f086a3ef0d02dbbd74b7bb2a5982e8a91da6d 100644
--- a/app/finders/user_recent_events_finder.rb
+++ b/app/finders/user_recent_events_finder.rb
@@ -7,6 +7,7 @@
 class UserRecentEventsFinder
   prepend FinderWithCrossProjectAccess
   include FinderMethods
+  include Gitlab::Allowable
 
   requires_cross_project_access
 
@@ -21,6 +22,8 @@ class UserRecentEventsFinder
   end
 
   def execute
+    return Event.none unless can?(current_user, :read_user_profile, target_user)
+
     recent_events(params[:offset] || 0)
       .joins(:project)
       .with_associations
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index d2daee22aba4fd09fcf85611b2f0cebdd5cfdb1c..18f0979fc86355a8b3394de3c6faedea525cc0f8 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -7,7 +7,7 @@ module AuthHelper
   end
 
   def omniauth_enabled?
-    Gitlab.config.omniauth.enabled
+    Gitlab::Auth.omniauth_enabled?
   end
 
   def provider_has_icon?(name)
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index 3605d6a3c959e875eeb64cc3dbee503e4b2c65de..0171a8801648c514478705bd8c7da3a78fea7e82 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -51,7 +51,7 @@ module ButtonHelper
     }
 
     content_tag :button, button_attributes do
-      concat(icon('clipboard', 'aria-hidden': 'true')) unless hide_button_icon
+      concat(sprite_icon('duplicate')) unless hide_button_icon
       concat(button_text)
     end
   end
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index f49b5c7b51abb53ca981cf0419bbdd083406d897..330959e536dc1f5983c06fe8cafc3fef661ae4de 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -56,7 +56,7 @@ module CiStatusHelper
     status.humanize
   end
 
-  def ci_icon_for_status(status)
+  def ci_icon_for_status(status, size: 16)
     if detailed_status?(status)
       return sprite_icon(status.icon)
     end
@@ -85,7 +85,7 @@ module CiStatusHelper
         'status_canceled'
       end
 
-    sprite_icon(icon_name, size: 16)
+    sprite_icon(icon_name, size: size)
   end
 
   def pipeline_status_cache_key(pipeline_status)
@@ -111,7 +111,8 @@ module CiStatusHelper
       'commit',
       commit.status(ref),
       path,
-      tooltip_placement: tooltip_placement)
+      tooltip_placement: tooltip_placement,
+      icon_size: 24)
   end
 
   def render_pipeline_status(pipeline, tooltip_placement: 'left')
@@ -125,16 +126,16 @@ module CiStatusHelper
       Ci::Runner.instance_type.blank?
   end
 
-  def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body')
+  def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16)
     klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}"
     title = "#{type.titleize}: #{ci_label_for_status(status)}"
     data = { toggle: 'tooltip', placement: tooltip_placement, container: container }
 
     if path
-      link_to ci_icon_for_status(status), path,
+      link_to ci_icon_for_status(status, size: icon_size), path,
               class: klass, title: title, data: data
     else
-      content_tag :span, ci_icon_for_status(status),
+      content_tag :span, ci_icon_for_status(status, size: icon_size),
               class: klass, title: title, data: data
     end
   end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index e5c3be47801b56ae1987ca947ae765c41de0e1d1..89fe90fd801d751e678b997dd101fec4c60f96f1 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -145,15 +145,14 @@ module CommitsHelper
         person_name
       end
 
-    options = {
-      class: "commit-#{options[:source]}-link has-tooltip",
-      title: source_email
+    link_options = {
+      class: "commit-#{options[:source]}-link"
     }
 
     if user.nil?
-      mail_to(source_email, text, options)
+      mail_to(source_email, text, link_options)
     else
-      link_to(text, user_path(user), options)
+      link_to(text, user_path(user), link_options)
     end
   end
 
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 221f1aa9dd8a74c72cc1828791a34dd99b6c3e01..aaf9dff43ee88b7f16b9fe61b847c8634fdee09f 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -63,10 +63,10 @@ module ProjectsHelper
     author_html = author_html.html_safe
 
     if opts[:name]
-      link_to(author_html, user_path(author), class: "author_link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
+      link_to(author_html, user_path(author), class: "author-link #{"#{opts[:extra_class]}" if opts[:extra_class]} #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
     else
       title = opts[:title].sub(":name", sanitize(author.name))
-      link_to(author_html, user_path(author), class: "author_link has-tooltip", title: title, data: { container: 'body' }).html_safe
+      link_to(author_html, user_path(author), class: "author-link has-tooltip", title: title, data: { container: 'body' }).html_safe
     end
   end
 
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index 4d17b22a4a1eae33b83e8bcb908268c40144a79a..8ee4203b6f5cb43ff555849f72298d5449f0e6b9 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -42,7 +42,13 @@ module UsersHelper
   private
 
   def get_profile_tabs
-    [:activity, :groups, :contributed, :projects, :snippets]
+    tabs = []
+
+    if can?(current_user, :read_user_profile, @user)
+      tabs += [:activity, :groups, :contributed, :projects, :snippets]
+    end
+
+    tabs
   end
 
   def get_current_user_menu_items
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index e395cda03d387a3db733e19acce70d526628bb6b..cf2fe5a2019dc7c07975c870336dad58a3b9fdf0 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -126,10 +126,9 @@ module VisibilityLevelHelper
   end
 
   def visibility_icon_description(form_model)
-    case form_model
-    when Project
+    if form_model.respond_to?(:visibility_level_allowed_as_fork?)
       project_visibility_icon_description(form_model.visibility_level)
-    when Group
+    elsif form_model.respond_to?(:visibility_level_allowed_by_sub_groups?)
       group_visibility_icon_description(form_model.visibility_level)
     end
   end
diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb
index 5082dc45368dc22cc68741d7d31182dce1b0f3bb..7ab647abe9333fb48d74614c038678647ab0b92f 100644
--- a/app/models/deploy_token.rb
+++ b/app/models/deploy_token.rb
@@ -27,7 +27,7 @@ class DeployToken < ActiveRecord::Base
   end
 
   def active?
-    !revoked
+    !revoked && expires_at > Date.today
   end
 
   def scopes
@@ -58,6 +58,10 @@ class DeployToken < ActiveRecord::Base
     write_attribute(:expires_at, value.presence || Forever.date)
   end
 
+  def admin?
+    false
+  end
+
   private
 
   def ensure_at_least_one_scope
diff --git a/app/models/project.rb b/app/models/project.rb
index 5f582dfa5ee1a53613b347472efff6e70a4f43d1..325dbd0197ffb1535d4a7136239cc371e1daf992 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -154,6 +154,7 @@ class Project < ActiveRecord::Base
   has_one :mock_monitoring_service
   has_one :microsoft_teams_service
   has_one :packagist_service
+  has_one :hangouts_chat_service
 
   # TODO: replace these relations with the fork network versions
   has_one  :forked_project_link,  foreign_key: "forked_to_project_id"
@@ -326,6 +327,7 @@ class Project < ActiveRecord::Base
   scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
   scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
   scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
+  scope :visible_to_user_and_access_level, ->(user, access_level) { where(id: user.authorized_projects.where('project_authorizations.access_level >= ?', access_level).select(:id).reorder(nil)) }
   scope :archived, -> { where(archived: true) }
   scope :non_archived, -> { where(archived: false) }
   scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
diff --git a/app/models/project_services/hangouts_chat_service.rb b/app/models/project_services/hangouts_chat_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a8512c5f57cce7e8d2770cb34ff8ca56a5ea2f2f
--- /dev/null
+++ b/app/models/project_services/hangouts_chat_service.rb
@@ -0,0 +1,67 @@
+require 'hangouts_chat'
+
+class HangoutsChatService < ChatNotificationService
+  def title
+    'Hangouts Chat'
+  end
+
+  def description
+    'Receive event notifications in Google Hangouts Chat'
+  end
+
+  def self.to_param
+    'hangouts_chat'
+  end
+
+  def help
+    'This service sends notifications about projects events to Google Hangouts Chat room.<br />
+    To set up this service:
+    <ol>
+      <li><a href="https://developers.google.com/hangouts/chat/how-tos/webhooks">Set up an incoming webhook for your room</a>. All notifications will come to this room.</li>
+      <li>Paste the <strong>Webhook URL</strong> into the field below.</li>
+      <li>Select events below to enable notifications.</li>
+    </ol>'
+  end
+
+  def event_field(event)
+  end
+
+  def default_channel_placeholder
+  end
+
+  def webhook_placeholder
+    'https://chat.googleapis.com/v1/spaces…'
+  end
+
+  def default_fields
+    [
+      { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
+      { type: 'checkbox', name: 'notify_only_broken_pipelines' },
+      { type: 'checkbox', name: 'notify_only_default_branch' }
+    ]
+  end
+
+  private
+
+  def notify(message, opts)
+    simple_text = parse_simple_text_message(message)
+    HangoutsChat::Sender.new(webhook).simple(simple_text)
+  end
+
+  def parse_simple_text_message(message)
+    header = message.pretext
+    return header if message.attachments.empty?
+
+    attachment = message.attachments.first
+    title      = format_attachment_title(attachment)
+    body       = attachment[:text]
+
+    [header, title, body].compact.join("\n")
+  end
+
+  def format_attachment_title(attachment)
+    return attachment[:title] unless attachment[:title_link]
+
+    "<#{attachment[:title_link]}|#{attachment[:title]}>"
+  end
+end
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 3aa56b3983f9c8d23d16c3bb2b73263207596435..f4b3421f04b95b23b949a02ab91283911485fdc6 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -82,7 +82,7 @@ class ProjectWiki
 
   # Returns an Array of Gitlab WikiPage instances or an
   # empty Array if this Wiki has no pages.
-  def pages(limit: nil)
+  def pages(limit: 0)
     wiki.pages(limit: limit).map { |page| WikiPage.new(self, page, true) }
   end
 
diff --git a/app/models/service.rb b/app/models/service.rb
index ad835293b46d4e8b753267b4f52bc80116bc3754..cbfe0c6eedd42b0bc5af54fd5b6037cdef7865f8 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -254,6 +254,7 @@ class Service < ActiveRecord::Base
       emails_on_push
       external_wiki
       flowdock
+      hangouts_chat
       hipchat
       irker
       jira
diff --git a/app/policies/application_setting/term_policy.rb b/app/policies/application_setting/term_policy.rb
index f03bf748c762af057296a19e4e1e9795933d541f..17f00f33d35251b3b1743b3d1782364c2ccabcb9 100644
--- a/app/policies/application_setting/term_policy.rb
+++ b/app/policies/application_setting/term_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ApplicationSetting
   class TermPolicy < BasePolicy
     include Gitlab::Utils::StrongMemoize
diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb
index 603218aa6df312d0b8eac7ce4f74d7d56393d065..0d0f1c28badd0a4e58c32bbb0d2c6d68fd52db3e 100644
--- a/app/policies/base_policy.rb
+++ b/app/policies/base_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require_dependency 'declarative_policy'
 
 class BasePolicy < DeclarativePolicy::Base
diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb
index 75c7e529902032ddb25b922646c8bed508d8658a..3858b29c82c090bb7c8bf29c5a1cfe27e6c4d356 100644
--- a/app/policies/ci/build_policy.rb
+++ b/app/policies/ci/build_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Ci
   class BuildPolicy < CommitStatusPolicy
     condition(:protected_ref) do
diff --git a/app/policies/ci/pipeline_policy.rb b/app/policies/ci/pipeline_policy.rb
index b81329d06256da33646c67948b156eb7634f2c15..f9623587957d711002c528ccc13941f171e02a09 100644
--- a/app/policies/ci/pipeline_policy.rb
+++ b/app/policies/ci/pipeline_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Ci
   class PipelinePolicy < BasePolicy
     delegate { @subject.project }
diff --git a/app/policies/ci/pipeline_schedule_policy.rb b/app/policies/ci/pipeline_schedule_policy.rb
index ecba0488d3c44f3c7e787910f01252a14ce044bf..cf3f784f851d20084bc29e2869c8e21ea2d58929 100644
--- a/app/policies/ci/pipeline_schedule_policy.rb
+++ b/app/policies/ci/pipeline_schedule_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Ci
   class PipelineSchedulePolicy < PipelinePolicy
     alias_method :pipeline_schedule, :subject
diff --git a/app/policies/ci/runner_policy.rb b/app/policies/ci/runner_policy.rb
index 895abe87d86290ce406f87bdb5cb19540c945a0e..c44f22b6ad3f11f7593547d87041376e088301d6 100644
--- a/app/policies/ci/runner_policy.rb
+++ b/app/policies/ci/runner_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Ci
   class RunnerPolicy < BasePolicy
     with_options scope: :subject, score: 0
diff --git a/app/policies/ci/trigger_policy.rb b/app/policies/ci/trigger_policy.rb
index 5592ac3081218e4fb3bf08fed43440af97e3189a..209db44539c9073092d0a0928e93ef7221dfcd29 100644
--- a/app/policies/ci/trigger_policy.rb
+++ b/app/policies/ci/trigger_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Ci
   class TriggerPolicy < BasePolicy
     delegate { @subject.project }
diff --git a/app/policies/clusters/cluster_policy.rb b/app/policies/clusters/cluster_policy.rb
index b5b24491655f9b9f966c317b1111ec16d019e00f..147943a3d6ce361bbc178d79e5c996e5244dc3ca 100644
--- a/app/policies/clusters/cluster_policy.rb
+++ b/app/policies/clusters/cluster_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Clusters
   class ClusterPolicy < BasePolicy
     alias_method :cluster, :subject
diff --git a/app/policies/commit_status_policy.rb b/app/policies/commit_status_policy.rb
index 24b2a4cc7fd7bb37cc51f86cc365cf1e975a1a74..eea2a24fb2d2698cc7896ec531c08193e517c17c 100644
--- a/app/policies/commit_status_policy.rb
+++ b/app/policies/commit_status_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class CommitStatusPolicy < BasePolicy
   delegate { @subject.project }
 
diff --git a/app/policies/deploy_key_policy.rb b/app/policies/deploy_key_policy.rb
index 62a22a59be6e93d32aafd085faf7b46be6aff94f..204c54a5b2036840c7ab916e7387785e59838e86 100644
--- a/app/policies/deploy_key_policy.rb
+++ b/app/policies/deploy_key_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DeployKeyPolicy < BasePolicy
   with_options scope: :subject, score: 0
   condition(:private_deploy_key) { @subject.private? }
diff --git a/app/policies/deploy_token_policy.rb b/app/policies/deploy_token_policy.rb
index d1b459cfc90d484e4791764b99232e88a8ea082b..e648df3edfc8abc373a04a06797b0fdb443b2ba9 100644
--- a/app/policies/deploy_token_policy.rb
+++ b/app/policies/deploy_token_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DeployTokenPolicy < BasePolicy
   with_options scope: :subject, score: 0
   condition(:maintainer) { @subject.project.team.maintainer?(@user) }
diff --git a/app/policies/deployment_policy.rb b/app/policies/deployment_policy.rb
index 62b63b9f87bb73dbe485f64e54d155608906fe1b..56ac898b6abdb7801edf7ef94755058840f1d48c 100644
--- a/app/policies/deployment_policy.rb
+++ b/app/policies/deployment_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DeploymentPolicy < BasePolicy
   delegate { @subject.project }
 end
diff --git a/app/policies/environment_policy.rb b/app/policies/environment_policy.rb
index 2d07311db723630acdd1c5e53b391537f6017140..d1243491f5a3614a9995008ecd4bb20a621c5c34 100644
--- a/app/policies/environment_policy.rb
+++ b/app/policies/environment_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class EnvironmentPolicy < BasePolicy
   delegate { @subject.project }
 
diff --git a/app/policies/external_issue_policy.rb b/app/policies/external_issue_policy.rb
index e031b38078cec3dae6303eb89f415c9f98ad2ed3..1106536e075cf4c467a59d69c49816dfe52851ab 100644
--- a/app/policies/external_issue_policy.rb
+++ b/app/policies/external_issue_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ExternalIssuePolicy < BasePolicy
   delegate { @subject.project }
 end
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index 1cf5515d9d73517356e9596736a378624bb32abb..6e3827736b2fe58917d13688256b6cae91a33bc5 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GlobalPolicy < BasePolicy
   desc "User is blocked"
   with_options scope: :user, score: 0
diff --git a/app/policies/group_label_policy.rb b/app/policies/group_label_policy.rb
index e3dd3296699250fa4391581f2b0ec1759d95ed8e..9f3acd44b2360773d7267ddfa15aa6dfeba42c45 100644
--- a/app/policies/group_label_policy.rb
+++ b/app/policies/group_label_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GroupLabelPolicy < BasePolicy
   delegate { @subject.group }
 end
diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb
index 23dd0d7cd23827d3a0089a24241171df29fccecf..6f1afb87c85f7f8d581ae710a3db4a09b9bcd7ff 100644
--- a/app/policies/group_member_policy.rb
+++ b/app/policies/group_member_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GroupMemberPolicy < BasePolicy
   delegate :group
 
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index dc339b71ec7755991a73a42a34a0b0e1552fa020..a8d7a05f50985596f5bb878f87d72f0262561f47 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GroupPolicy < BasePolicy
   desc "Group is public"
   with_options scope: :subject, score: 0
diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb
index b431d376e3dd8a2d5c957349d25c0a56bddfc7f2..198bb168d858604006b3ccca209e9e9883f9d41a 100644
--- a/app/policies/issuable_policy.rb
+++ b/app/policies/issuable_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class IssuablePolicy < BasePolicy
   delegate { @subject.project }
 
diff --git a/app/policies/issue_policy.rb b/app/policies/issue_policy.rb
index 263c6e3039c2576b25b70c46368aed6005bbdd8a..94b5f37c682f7b2ae79ab82228545f123553ce9b 100644
--- a/app/policies/issue_policy.rb
+++ b/app/policies/issue_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class IssuePolicy < IssuablePolicy
   # This class duplicates the same check of Issue#readable_by? for performance reasons
   # Make sure to sync this class checks with issue.rb to avoid security problems.
diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb
index c3fe857f8a2dd85fc490e603335f27d25e83dc89..a2950951d0352b5849cdd811fa030e554972e9ff 100644
--- a/app/policies/merge_request_policy.rb
+++ b/app/policies/merge_request_policy.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
 class MergeRequestPolicy < IssuablePolicy
 end
diff --git a/app/policies/namespace_policy.rb b/app/policies/namespace_policy.rb
index eb01218eb0aeea0fca83f57cd894d5261b7e1073..2babcb0a2d9440cec9923f46dca99d795f5db740 100644
--- a/app/policies/namespace_policy.rb
+++ b/app/policies/namespace_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class NamespacePolicy < BasePolicy
   rule { anonymous }.prevent_all
 
diff --git a/app/policies/nil_policy.rb b/app/policies/nil_policy.rb
index 13f46ba60f086a4ba8f7a3d5552a561f44e8560d..fc969f8cd05bbd3baa92b591195329d16de768c6 100644
--- a/app/policies/nil_policy.rb
+++ b/app/policies/nil_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class NilPolicy < BasePolicy
   rule { default }.prevent_all
 end
diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb
index 077a6761ee6c400858c9280d13948e497babe9b1..bbc2b48b8567e24813d0305c871df7a22d96b545 100644
--- a/app/policies/note_policy.rb
+++ b/app/policies/note_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class NotePolicy < BasePolicy
   delegate { @subject.project }
   delegate { @subject.noteable if DeclarativePolicy.has_policy?(@subject.noteable) }
diff --git a/app/policies/personal_snippet_policy.rb b/app/policies/personal_snippet_policy.rb
index c1a84727cfa5489fd8714921ecdbd61d04b98e17..777f933cdcd839fb69c3ae03e52ea372bbf29787 100644
--- a/app/policies/personal_snippet_policy.rb
+++ b/app/policies/personal_snippet_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class PersonalSnippetPolicy < BasePolicy
   condition(:public_snippet, scope: :subject) { @subject.public? }
   condition(:is_author) { @user && @subject.author == @user }
diff --git a/app/policies/project_label_policy.rb b/app/policies/project_label_policy.rb
index 2d0f021118b289188e3f7f63d89bab970c466fd2..5ce896ecaf2e3bb7ad95dbb2aeace6d2b06a3d39 100644
--- a/app/policies/project_label_policy.rb
+++ b/app/policies/project_label_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectLabelPolicy < BasePolicy
   delegate { @subject.project }
 end
diff --git a/app/policies/project_member_policy.rb b/app/policies/project_member_policy.rb
index 9aedb620be96a6f0c611e186e3f8e19b9ef6a725..f2f18406bd35b178f368c1526fd87877749a34fc 100644
--- a/app/policies/project_member_policy.rb
+++ b/app/policies/project_member_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectMemberPolicy < BasePolicy
   delegate { @subject.project }
 
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index bc49092633f163175e35c82e2c6a680064c85dd6..f52a3bad77d3aa95bc015507079283d290d3b6c5 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectPolicy < BasePolicy
   extend ClassMethods
 
diff --git a/app/policies/project_policy/class_methods.rb b/app/policies/project_policy/class_methods.rb
index 60e5aba00baa68df5e7f9e60f798dca58d7f53e5..42d993406a978c27bb0a1da736062f96b222a7c6 100644
--- a/app/policies/project_policy/class_methods.rb
+++ b/app/policies/project_policy/class_methods.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectPolicy
   module ClassMethods
     def create_read_update_admin_destroy(name)
diff --git a/app/policies/project_snippet_policy.rb b/app/policies/project_snippet_policy.rb
index dd270643bbf09d83d24d22a711421ba7eababeb4..288bf070cfc7d493ae91f0bcc94108fc4d2c18cd 100644
--- a/app/policies/project_snippet_policy.rb
+++ b/app/policies/project_snippet_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectSnippetPolicy < BasePolicy
   delegate :project
 
diff --git a/app/policies/protected_branch_policy.rb b/app/policies/protected_branch_policy.rb
index 1a7faa4db4047607905ecf1a6b2638e93e32d4d6..0e83d2e5834eff01d00358a54b39d01ea6d2b23c 100644
--- a/app/policies/protected_branch_policy.rb
+++ b/app/policies/protected_branch_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProtectedBranchPolicy < BasePolicy
   delegate { @subject.project }
 
diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb
index ee219f0a0d0f0547839368b7e5a8de5899f6823d..b5717029354c839e0a87ad037c14787ce6ebb469 100644
--- a/app/policies/user_policy.rb
+++ b/app/policies/user_policy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class UserPolicy < BasePolicy
   desc "The current user is the user in question"
   condition(:user_is_self, score: 0) { @subject == @user }
@@ -5,6 +7,9 @@ class UserPolicy < BasePolicy
   desc "This is the ghost user"
   condition(:subject_ghost, scope: :subject, score: 0) { @subject.ghost? }
 
+  desc "The profile is private"
+  condition(:private_profile, scope: :subject, score: 0) { @subject.private_profile? }
+
   rule { ~restricted_public_level }.enable :read_user
   rule { ~anonymous }.enable :read_user
 
@@ -12,4 +17,7 @@ class UserPolicy < BasePolicy
     enable :destroy_user
     enable :update_user
   end
+
+  rule { default }.enable :read_user_profile
+  rule { private_profile & ~(user_is_self | admin) }.prevent :read_user_profile
 end
diff --git a/app/presenters/ci/build_metadata_presenter.rb b/app/presenters/ci/build_metadata_presenter.rb
index 5048f967ea862cafce4653c82904a64708b7dce2..015b1f67db7217eec267350bfadf25236f2f7f0a 100644
--- a/app/presenters/ci/build_metadata_presenter.rb
+++ b/app/presenters/ci/build_metadata_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Ci
   class BuildMetadataPresenter < Gitlab::View::Presenter::Delegated
     TIMEOUT_SOURCES = {
diff --git a/app/presenters/ci/build_presenter.rb b/app/presenters/ci/build_presenter.rb
index e0aaa5cb73630811ec56f895da32c7418f100f17..5331cdf632bc435bbb3603486dee7d8e18f1dd89 100644
--- a/app/presenters/ci/build_presenter.rb
+++ b/app/presenters/ci/build_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Ci
   class BuildPresenter < CommitStatusPresenter
     def erased_by_user?
diff --git a/app/presenters/ci/group_variable_presenter.rb b/app/presenters/ci/group_variable_presenter.rb
index 98d68bc7a837c904d74050127472ca3b02d8802a..99011150c84e0f9166e93db89aaf5534623ddf9f 100644
--- a/app/presenters/ci/group_variable_presenter.rb
+++ b/app/presenters/ci/group_variable_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Ci
   class GroupVariablePresenter < Gitlab::View::Presenter::Delegated
     presents :variable
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index cc2bce9862d96d75fca58994c193ee2b5e038172..93a38f92073c23e36945fe4aafecc6bffe67cd26 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Ci
   class PipelinePresenter < Gitlab::View::Presenter::Delegated
     include Gitlab::Utils::StrongMemoize
diff --git a/app/presenters/ci/variable_presenter.rb b/app/presenters/ci/variable_presenter.rb
index 96159f88c5928a014b04b562346f30c34e2dd2d8..f027f3aa5600fa992cd24aa58efe51c9431fcaf3 100644
--- a/app/presenters/ci/variable_presenter.rb
+++ b/app/presenters/ci/variable_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Ci
   class VariablePresenter < Gitlab::View::Presenter::Delegated
     presents :variable
diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb
index a424da5ab240e60f4a4089cd520693b67a1e39ee..dfdd8e82f970d71c8de8c2dd153afccb804c20ee 100644
--- a/app/presenters/clusters/cluster_presenter.rb
+++ b/app/presenters/clusters/cluster_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Clusters
   class ClusterPresenter < Gitlab::View::Presenter::Delegated
     presents :cluster
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index 9a7aaf4ef32600b60020c35f996179a1aed3f41f..3a9088cfcb82a4d44bbcea13c32b6f2b5f2e9f50 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
   CALLOUT_FAILURE_MESSAGES = {
       unknown_failure: 'There is an unknown failure, please try again',
diff --git a/app/presenters/conversational_development_index/metric_presenter.rb b/app/presenters/conversational_development_index/metric_presenter.rb
index bb65ba2646b94a46ffa1e1947cb432735046f77d..e0312c6f43181d277ad3c73889a2411c333f15e3 100644
--- a/app/presenters/conversational_development_index/metric_presenter.rb
+++ b/app/presenters/conversational_development_index/metric_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module ConversationalDevelopmentIndex
   class MetricPresenter < Gitlab::View::Presenter::Simple
     def cards
diff --git a/app/presenters/generic_commit_status_presenter.rb b/app/presenters/generic_commit_status_presenter.rb
index da09df29a37c8880255470d1cf8dc127b6866f7a..a1dc72d5b98a3963315d7afdd6fe003d6e191509 100644
--- a/app/presenters/generic_commit_status_presenter.rb
+++ b/app/presenters/generic_commit_status_presenter.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
 class GenericCommitStatusPresenter < CommitStatusPresenter
 end
diff --git a/app/presenters/group_member_presenter.rb b/app/presenters/group_member_presenter.rb
index 8f53dfa105e81e5e30366826f8af2dd98278a132..c4dcc9e60f9757efb28e991cb9adff8fe1eced06 100644
--- a/app/presenters/group_member_presenter.rb
+++ b/app/presenters/group_member_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GroupMemberPresenter < MemberPresenter
   private
 
diff --git a/app/presenters/member_presenter.rb b/app/presenters/member_presenter.rb
index 7d2f9303b8f4fb4185faad37f57ae68726ff2666..2497bea4aff98b4da9e0f6691154f7c1132e5997 100644
--- a/app/presenters/member_presenter.rb
+++ b/app/presenters/member_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MemberPresenter < Gitlab::View::Presenter::Delegated
   presents :member
 
diff --git a/app/presenters/members_presenter.rb b/app/presenters/members_presenter.rb
index e4aba37b69e85607273a26da7c6f6106e31be88b..03ebea36d49b4deb0c7fec606bc68a80a8945067 100644
--- a/app/presenters/members_presenter.rb
+++ b/app/presenters/members_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MembersPresenter < Gitlab::View::Presenter::Delegated
   include Enumerable
 
diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb
index f77b35416445db293e51d3a56a42155df78c2d1e..ffa238a63d58e04434c68ab0a1a07e86486fb18a 100644
--- a/app/presenters/merge_request_presenter.rb
+++ b/app/presenters/merge_request_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
   include ActionView::Helpers::UrlHelper
   include GitlabRoutingHelper
diff --git a/app/presenters/project_member_presenter.rb b/app/presenters/project_member_presenter.rb
index 7f42d2b70df9b6ceafca6269c02f51fc93cc43b3..e4731074e86183b4b06bf84a6885803242c4e1ff 100644
--- a/app/presenters/project_member_presenter.rb
+++ b/app/presenters/project_member_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectMemberPresenter < MemberPresenter
   private
 
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index d4d622d84ab95cc7a82e996a9295658b8f3967e0..4c2f33213d699cf59b6bf7acf3cea83c2946be0c 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectPresenter < Gitlab::View::Presenter::Delegated
   include ActionView::Helpers::NumberHelper
   include ActionView::Helpers::UrlHelper
diff --git a/app/presenters/projects/settings/deploy_keys_presenter.rb b/app/presenters/projects/settings/deploy_keys_presenter.rb
index c226586fba5ee57ee67dfddbb71e2bcbe7b33765..28eaef00a12981830216dcb1a1beb77bb353e6b7 100644
--- a/app/presenters/projects/settings/deploy_keys_presenter.rb
+++ b/app/presenters/projects/settings/deploy_keys_presenter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Projects
   module Settings
     class DeployKeysPresenter < Gitlab::View::Presenter::Simple
diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb
index bdc22d7120261871d3e6426e1e57ff6d63a0ed92..99663c8d5ebdf38a58fc70af0d7c9b8983132226 100644
--- a/app/serializers/analytics_build_entity.rb
+++ b/app/serializers/analytics_build_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsBuildEntity < Grape::Entity
   include RequestAwareEntity
   include EntityDateHelper
diff --git a/app/serializers/analytics_build_serializer.rb b/app/serializers/analytics_build_serializer.rb
index f172d67d35627639640271e2d09acc847f3a122d..9c9f76a1c28cd07c6c7c5e04548b9372e0646af7 100644
--- a/app/serializers/analytics_build_serializer.rb
+++ b/app/serializers/analytics_build_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsBuildSerializer < BaseSerializer
   entity AnalyticsBuildEntity
 end
diff --git a/app/serializers/analytics_commit_entity.rb b/app/serializers/analytics_commit_entity.rb
index 402cecbfd087cf80bc05f2348b6ba57d26ccc87c..b25c603c9f0bb8706acf044483de56e57046fd76 100644
--- a/app/serializers/analytics_commit_entity.rb
+++ b/app/serializers/analytics_commit_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsCommitEntity < CommitEntity
   include EntityDateHelper
 
diff --git a/app/serializers/analytics_commit_serializer.rb b/app/serializers/analytics_commit_serializer.rb
index cdbfecf2b70aab22f6c5dfc68b01423b4a3b794b..0f65687a3c04701f3137d07cc03e41c24919ce1e 100644
--- a/app/serializers/analytics_commit_serializer.rb
+++ b/app/serializers/analytics_commit_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsCommitSerializer < BaseSerializer
   entity AnalyticsCommitEntity
 end
diff --git a/app/serializers/analytics_generic_serializer.rb b/app/serializers/analytics_generic_serializer.rb
index 9f4859e841034aae52f1ccc9dad506121f0f6fb0..10391c130347f4a107e94a16840e67b8d9efe048 100644
--- a/app/serializers/analytics_generic_serializer.rb
+++ b/app/serializers/analytics_generic_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsGenericSerializer < BaseSerializer
   def represent(resource, opts = {})
     resource.symbolize_keys!
diff --git a/app/serializers/analytics_issue_entity.rb b/app/serializers/analytics_issue_entity.rb
index b7d95ea020f4fdf127e0574fca5f641fa47488d8..ab15bd0ac7a92697eccff9f1918f08fd2fa0f971 100644
--- a/app/serializers/analytics_issue_entity.rb
+++ b/app/serializers/analytics_issue_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsIssueEntity < Grape::Entity
   include RequestAwareEntity
   include EntityDateHelper
diff --git a/app/serializers/analytics_issue_serializer.rb b/app/serializers/analytics_issue_serializer.rb
index 4fb3e8f1bb4a9bf6126906c898089dde300a4f0d..4a1777276a47c0f0bd55d04b467a485a6ca56f07 100644
--- a/app/serializers/analytics_issue_serializer.rb
+++ b/app/serializers/analytics_issue_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsIssueSerializer < AnalyticsGenericSerializer
   entity AnalyticsIssueEntity
 end
diff --git a/app/serializers/analytics_merge_request_entity.rb b/app/serializers/analytics_merge_request_entity.rb
index 888265eaa3853f69e9aad16136363880613acaf3..b7134da94615c28d42a9f9612b3e2609b9b15e59 100644
--- a/app/serializers/analytics_merge_request_entity.rb
+++ b/app/serializers/analytics_merge_request_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsMergeRequestEntity < AnalyticsIssueEntity
   expose :state
 
diff --git a/app/serializers/analytics_merge_request_serializer.rb b/app/serializers/analytics_merge_request_serializer.rb
index 4622a1dd8558459ece3da486453b16004d21e48f..f0b9115d02cd04d79cf9b1521c290d01b12a5276 100644
--- a/app/serializers/analytics_merge_request_serializer.rb
+++ b/app/serializers/analytics_merge_request_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsMergeRequestSerializer < AnalyticsGenericSerializer
   entity AnalyticsMergeRequestEntity
 end
diff --git a/app/serializers/analytics_stage_entity.rb b/app/serializers/analytics_stage_entity.rb
index 3e355a13e066d4b90ac8d3e375572c0d8d3dac3e..ae7c20c3bbaad3b00b40cca632b224ce1a3aa763 100644
--- a/app/serializers/analytics_stage_entity.rb
+++ b/app/serializers/analytics_stage_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsStageEntity < Grape::Entity
   include EntityDateHelper
 
diff --git a/app/serializers/analytics_stage_serializer.rb b/app/serializers/analytics_stage_serializer.rb
index 613cf6874d866750d4497ae0630797008779eece..86786273240396a84e7cf077d9f3fa924e2ed0b2 100644
--- a/app/serializers/analytics_stage_serializer.rb
+++ b/app/serializers/analytics_stage_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsStageSerializer < BaseSerializer
   entity AnalyticsStageEntity
 end
diff --git a/app/serializers/analytics_summary_entity.rb b/app/serializers/analytics_summary_entity.rb
index 9c37afd53e1ac5972757443b531f5f33047848ec..39c6b4b06b22d4d05278ad939eee8aa08be31126 100644
--- a/app/serializers/analytics_summary_entity.rb
+++ b/app/serializers/analytics_summary_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsSummaryEntity < Grape::Entity
   expose :value, safe: true
   expose :title
diff --git a/app/serializers/analytics_summary_serializer.rb b/app/serializers/analytics_summary_serializer.rb
index c87a24aa47c0df78f7bd40709cd151497abfc47e..b22bd737f03a5900d7742a9ad89c7789c528317e 100644
--- a/app/serializers/analytics_summary_serializer.rb
+++ b/app/serializers/analytics_summary_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AnalyticsSummarySerializer < BaseSerializer
   entity AnalyticsSummaryEntity
 end
diff --git a/app/serializers/award_emoji_entity.rb b/app/serializers/award_emoji_entity.rb
index 6e03cd02392b2b34ddab529b3664bd0afb9f5461..212931a2fa9611d15cbf44221a72b0d01001c502 100644
--- a/app/serializers/award_emoji_entity.rb
+++ b/app/serializers/award_emoji_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AwardEmojiEntity < Grape::Entity
   expose :name
   expose :user, using: API::Entities::UserSafe
diff --git a/app/serializers/base_serializer.rb b/app/serializers/base_serializer.rb
index 8cade280b0c633d712723661bf6e6f7711208ca3..7b65bd22f54a95c7811b6fc6de483815c1aa5cc0 100644
--- a/app/serializers/base_serializer.rb
+++ b/app/serializers/base_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class BaseSerializer
   attr_reader :params
 
diff --git a/app/serializers/blob_entity.rb b/app/serializers/blob_entity.rb
index b501fd5e964845ff793f7f8daf112dcda4cff2c7..3ac61481deab3b50c1d6e95a1250c124b8276c49 100644
--- a/app/serializers/blob_entity.rb
+++ b/app/serializers/blob_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class BlobEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/build_action_entity.rb b/app/serializers/build_action_entity.rb
index f2d76a8ad813b1f0d3854e74b407a3236f62f299..f9da3f639115adde848ffbaa685cac76015d3754 100644
--- a/app/serializers/build_action_entity.rb
+++ b/app/serializers/build_action_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class BuildActionEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/build_artifact_entity.rb b/app/serializers/build_artifact_entity.rb
index 6e0e33bc09b758d48b5ea437dd405d9c5caebe53..414f436e76ecc74efe5990ede500f5282352bd83 100644
--- a/app/serializers/build_artifact_entity.rb
+++ b/app/serializers/build_artifact_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class BuildArtifactEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index 2de9624aed407573ff5254fa409e9d67739f4b1d..b887b99d31c3bda5f4739eae65f3b6df54115e1c 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class BuildDetailsEntity < JobEntity
   expose :coverage, :erased_at, :duration
   expose :tag_list, as: :tags
diff --git a/app/serializers/build_metadata_entity.rb b/app/serializers/build_metadata_entity.rb
index f16f3badffa0a8cfaec0c3417c6f10e15fcfd4cb..6242ee8957d5c7b121b0595c8c9cea981f4d9b25 100644
--- a/app/serializers/build_metadata_entity.rb
+++ b/app/serializers/build_metadata_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class BuildMetadataEntity < Grape::Entity
   expose :timeout_human_readable
   expose :timeout_source do |metadata|
diff --git a/app/serializers/build_serializer.rb b/app/serializers/build_serializer.rb
index bae9932847f272e701872a3882ff36f8f4966367..0649fdad6a89afdc6faa303f85ed9e303bc9916e 100644
--- a/app/serializers/build_serializer.rb
+++ b/app/serializers/build_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class BuildSerializer < BaseSerializer
   entity JobEntity
 
diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb
index 77fc3336521af9dfa1c6aec37b674f9f1bfa0732..2bd17e58086d66cce20b5c919c2c7a2e3017ec8b 100644
--- a/app/serializers/cluster_application_entity.rb
+++ b/app/serializers/cluster_application_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ClusterApplicationEntity < Grape::Entity
   expose :name
   expose :status_name, as: :status
diff --git a/app/serializers/cluster_entity.rb b/app/serializers/cluster_entity.rb
index 7e5b0997878368d8a579eba978d84f56547bff1e..c59f68bbc4966298a39929600bd3fef7370f21bb 100644
--- a/app/serializers/cluster_entity.rb
+++ b/app/serializers/cluster_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ClusterEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/cluster_serializer.rb b/app/serializers/cluster_serializer.rb
index 2e13c1501e78988d707a1215dd6296c20fe35ac6..4bb4d4880d4363c2cdd3204abdbd2a3e592d8025 100644
--- a/app/serializers/cluster_serializer.rb
+++ b/app/serializers/cluster_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ClusterSerializer < BaseSerializer
   entity ClusterEntity
 
diff --git a/app/serializers/cohort_activity_month_entity.rb b/app/serializers/cohort_activity_month_entity.rb
index e6788a8b5966869c4b1da64cc66adc0eaf014613..cdbc89a2dcd14d2d2328c75e8f2a14bc7c88b213 100644
--- a/app/serializers/cohort_activity_month_entity.rb
+++ b/app/serializers/cohort_activity_month_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class CohortActivityMonthEntity < Grape::Entity
   include ActionView::Helpers::NumberHelper
 
diff --git a/app/serializers/cohort_entity.rb b/app/serializers/cohort_entity.rb
index 7cdba5b04846039a6567ca0397f7612ed0eed7ff..3d0213e1038f096d1edce8405836a64fec83ab22 100644
--- a/app/serializers/cohort_entity.rb
+++ b/app/serializers/cohort_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class CohortEntity < Grape::Entity
   include ActionView::Helpers::NumberHelper
 
diff --git a/app/serializers/cohorts_entity.rb b/app/serializers/cohorts_entity.rb
index 98f5995ba6f760178b1790b8e326c16987d6253d..a84d568bf30f82d1b395d100db8f9bfc6c196e45 100644
--- a/app/serializers/cohorts_entity.rb
+++ b/app/serializers/cohorts_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class CohortsEntity < Grape::Entity
   expose :months_included
   expose :cohorts, using: CohortEntity
diff --git a/app/serializers/cohorts_serializer.rb b/app/serializers/cohorts_serializer.rb
index fe9367b13d895951dcd0041b12d0a34050763e11..ceca5e1e5a8dabaf65561a529fa4118ad20615c4 100644
--- a/app/serializers/cohorts_serializer.rb
+++ b/app/serializers/cohorts_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class CohortsSerializer < AnalyticsGenericSerializer
   entity CohortsEntity
 end
diff --git a/app/serializers/commit_entity.rb b/app/serializers/commit_entity.rb
index c8dd98cc04d96177b15928829638fb5070981149..b3287c66554cca42bdc471a7230fafd934dbe936 100644
--- a/app/serializers/commit_entity.rb
+++ b/app/serializers/commit_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class CommitEntity < API::Entities::Commit
   include RequestAwareEntity
 
diff --git a/app/serializers/concerns/with_pagination.rb b/app/serializers/concerns/with_pagination.rb
index 89631b73fcfff16d9e2afaca16bf5c8801e87a10..c8ffae355e85376d7ef973474215a62e4fea9061 100644
--- a/app/serializers/concerns/with_pagination.rb
+++ b/app/serializers/concerns/with_pagination.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module WithPagination
   attr_accessor :paginator
 
diff --git a/app/serializers/container_repositories_serializer.rb b/app/serializers/container_repositories_serializer.rb
index 56dc70b568702adf8ef1c7b2c9328f9f58eab96c..e1ce3c7b3ae0dea76ccee4b925c7a4e20dc4e836 100644
--- a/app/serializers/container_repositories_serializer.rb
+++ b/app/serializers/container_repositories_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ContainerRepositoriesSerializer < BaseSerializer
   entity ContainerRepositoryEntity
 end
diff --git a/app/serializers/container_repository_entity.rb b/app/serializers/container_repository_entity.rb
index 1103cf30a07d5990ba09296a9e1c9bbed083b117..59bf35f5affc095e8296e82a092ccf897d24d4ac 100644
--- a/app/serializers/container_repository_entity.rb
+++ b/app/serializers/container_repository_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ContainerRepositoryEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/container_tag_entity.rb b/app/serializers/container_tag_entity.rb
index 8f1488e6cbb8f0da429d5cebda236b87ce346f5d..637294877f82f5dd97a7f17ab08377c87f3abc88 100644
--- a/app/serializers/container_tag_entity.rb
+++ b/app/serializers/container_tag_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ContainerTagEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/container_tags_serializer.rb b/app/serializers/container_tags_serializer.rb
index 6ff3adff135e642f623f7386bed5d5e91cc151ca..982ce33f6e33a362e2d3e30abe404a28057b20ae 100644
--- a/app/serializers/container_tags_serializer.rb
+++ b/app/serializers/container_tags_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ContainerTagsSerializer < BaseSerializer
   entity ContainerTagEntity
 
diff --git a/app/serializers/deploy_key_entity.rb b/app/serializers/deploy_key_entity.rb
index 2678f99510c0e80edc4c091488e794b204925fbe..54bf030aba1d2a50d44b62be6f201adf80c4daed 100644
--- a/app/serializers/deploy_key_entity.rb
+++ b/app/serializers/deploy_key_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DeployKeyEntity < Grape::Entity
   expose :id
   expose :user_id
diff --git a/app/serializers/deploy_key_serializer.rb b/app/serializers/deploy_key_serializer.rb
index 8f849eb88b717e132fb57b98d52d5ec4acb31253..a1cd98b631b4c6b2dcf4c09ccc6571dc9ea13884 100644
--- a/app/serializers/deploy_key_serializer.rb
+++ b/app/serializers/deploy_key_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DeployKeySerializer < BaseSerializer
   entity DeployKeyEntity
 end
diff --git a/app/serializers/deploy_keys_project_entity.rb b/app/serializers/deploy_keys_project_entity.rb
index 568ef5ab75e3859cffebcd1216dbde6e8474226d..5775ad72d0d57717ea97f94a1679b5f5c37c3f5c 100644
--- a/app/serializers/deploy_keys_project_entity.rb
+++ b/app/serializers/deploy_keys_project_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DeployKeysProjectEntity < Grape::Entity
   expose :can_push
   expose :project, using: ProjectEntity
diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb
index 241c689bccdd6822630b198c0a1101946da9d9ca..344148a1fb76754da38e48bc6c3fc3b2cb4b0bfc 100644
--- a/app/serializers/deployment_entity.rb
+++ b/app/serializers/deployment_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DeploymentEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/deployment_serializer.rb b/app/serializers/deployment_serializer.rb
index cba5c3f311f77f9ad3e5a84bedb7381639b86fcc..04db6b884896ecfa33dc22c07425fb148c4e8440 100644
--- a/app/serializers/deployment_serializer.rb
+++ b/app/serializers/deployment_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DeploymentSerializer < BaseSerializer
   entity DeploymentEntity
 
diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb
index 61135fba97bc07fc1e819a29685746274e4fda12..79844c9210a2f6fcd3cf67acc8af1be9862e8118 100644
--- a/app/serializers/diff_file_entity.rb
+++ b/app/serializers/diff_file_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DiffFileEntity < Grape::Entity
   include RequestAwareEntity
   include BlobHelper
diff --git a/app/serializers/diffs_entity.rb b/app/serializers/diffs_entity.rb
index bb804e5347a166e1f6d242fab35a7ae5121230ec..f75ace14d9cad08e1ea73aa889c299c62eb0fa95 100644
--- a/app/serializers/diffs_entity.rb
+++ b/app/serializers/diffs_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DiffsEntity < Grape::Entity
   include DiffHelper
   include RequestAwareEntity
diff --git a/app/serializers/diffs_serializer.rb b/app/serializers/diffs_serializer.rb
index 6771e10c5acdfe10d3c2db367ec45d71bde1bd26..9e5eb1699d41be06ac52b871b60537e1694fc9cc 100644
--- a/app/serializers/diffs_serializer.rb
+++ b/app/serializers/diffs_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DiffsSerializer < BaseSerializer
   entity DiffsEntity
 end
diff --git a/app/serializers/discussion_entity.rb b/app/serializers/discussion_entity.rb
index 7505bbdeb3d4335220cc956777402916683e490c..6f95e6f9ca1582c56999da1dc2072c4d3b53273d 100644
--- a/app/serializers/discussion_entity.rb
+++ b/app/serializers/discussion_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DiscussionEntity < Grape::Entity
   include RequestAwareEntity
   include NotesHelper
diff --git a/app/serializers/discussion_serializer.rb b/app/serializers/discussion_serializer.rb
index ed5e1224bb23b58c5f63cf0f396d131e2a8029e7..5be40e74175f38c7da8eea3bb4289cb1695ac92e 100644
--- a/app/serializers/discussion_serializer.rb
+++ b/app/serializers/discussion_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DiscussionSerializer < BaseSerializer
   entity DiscussionEntity
 end
diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb
index 464217123b4f10af415955f2c64c14eb1e405bc6..cc0c2abf8632c1e3290e7a56f0ea0a40b7e24514 100644
--- a/app/serializers/entity_date_helper.rb
+++ b/app/serializers/entity_date_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module EntityDateHelper
   include ActionView::Helpers::DateHelper
   include ActionView::Helpers::TagHelper
@@ -50,15 +52,20 @@ module EntityDateHelper
     elsif entity.due_date
       is_upcoming = (entity.due_date - Date.today).to_i > 0
       time_ago = time_ago_in_words(entity.due_date)
-      content = time_ago.gsub(/\d+/) { |match| "<strong>#{match}</strong>" }
-      content.slice!("about ")
-      content << " " + (is_upcoming ? _("remaining") : _("ago"))
-      content.html_safe
+
+      # https://gitlab.com/gitlab-org/gitlab-ce/issues/49440
+      #
+      # Need to improve the i18n here and do a full translation
+      # of the string instead of piecewise translations.
+      content = time_ago
+        .gsub(/\d+/) { |match| "<strong>#{match}</strong>" }
+        .remove("about ")
+      remaining_or_ago = is_upcoming ? _("remaining") : _("ago")
+
+      "#{content} #{remaining_or_ago}".html_safe
     elsif entity.start_date && entity.start_date.past?
-      days    = entity.elapsed_days
-      content = content_tag(:strong, days)
-      content << " #{'day'.pluralize(days)} elapsed"
-      content.html_safe
+      days = entity.elapsed_days
+      "#{content_tag(:strong, days)} #{'day'.pluralize(days)} elapsed".html_safe
     end
   end
 end
diff --git a/app/serializers/entity_request.rb b/app/serializers/entity_request.rb
index 456ba1174c0634486d37a9bb036a252b17c2e674..49e026e8c2ae471e14fc1063d729b2114c49e4a8 100644
--- a/app/serializers/entity_request.rb
+++ b/app/serializers/entity_request.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class EntityRequest
   # We use EntityRequest object to collect parameters and variables
   # from the controller. Because options that are being passed to the entity
diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb
index 83558fc66599011da42e7c0452bec91ad33e0cc3..b18e9706db6e787f469aa212a7a78ebbfabfe9de 100644
--- a/app/serializers/environment_entity.rb
+++ b/app/serializers/environment_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class EnvironmentEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb
index 84722f33f59d6c400735ca356dece4e3fce3e42d..dc1686c30c4a4be23ca62397d309c60d145438b1 100644
--- a/app/serializers/environment_serializer.rb
+++ b/app/serializers/environment_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class EnvironmentSerializer < BaseSerializer
   include WithPagination
 
diff --git a/app/serializers/group_child_entity.rb b/app/serializers/group_child_entity.rb
index ee150eefd9ec2bc3824c3b4aa5a6bf3326630608..f6804fe7f6a728e47d30f14c0c89a6413c2f781d 100644
--- a/app/serializers/group_child_entity.rb
+++ b/app/serializers/group_child_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GroupChildEntity < Grape::Entity
   include ActionView::Helpers::NumberHelper
   include RequestAwareEntity
diff --git a/app/serializers/group_child_serializer.rb b/app/serializers/group_child_serializer.rb
index 2baef0a57034a689d1f7f57d37e2674372f82bd3..789707c2c9bbac81c54a4c64a5cab1cefe8ff7e0 100644
--- a/app/serializers/group_child_serializer.rb
+++ b/app/serializers/group_child_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GroupChildSerializer < BaseSerializer
   include WithPagination
 
diff --git a/app/serializers/group_entity.rb b/app/serializers/group_entity.rb
index 6d8466da902d6c6c834693b80fa4cb7502830060..c46c342ee5ddf3817620ff99a3090ec8a42aedad 100644
--- a/app/serializers/group_entity.rb
+++ b/app/serializers/group_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GroupEntity < Grape::Entity
   include ActionView::Helpers::NumberHelper
   include RequestAwareEntity
diff --git a/app/serializers/group_serializer.rb b/app/serializers/group_serializer.rb
index 8cf7eb63bcf0123194b16a00b6d5f4e87fbe075c..38c5757a6c1ce6d6725c5747e311abf9f2ac7e1f 100644
--- a/app/serializers/group_serializer.rb
+++ b/app/serializers/group_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GroupSerializer < BaseSerializer
   include WithPagination
 
diff --git a/app/serializers/group_variable_entity.rb b/app/serializers/group_variable_entity.rb
index 62cf0b21e1e3df90443fa4f7a81638313468e3b7..0edab4a309257b6b6f30ab22340aff348ec05cb8 100644
--- a/app/serializers/group_variable_entity.rb
+++ b/app/serializers/group_variable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GroupVariableEntity < Grape::Entity
   expose :id
   expose :key
diff --git a/app/serializers/group_variable_serializer.rb b/app/serializers/group_variable_serializer.rb
index 8f8205924aadaaa20cd343c7f233c757da010a04..ed20b240cce5d90fbaab822f95947d352b78272f 100644
--- a/app/serializers/group_variable_serializer.rb
+++ b/app/serializers/group_variable_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class GroupVariableSerializer < BaseSerializer
   entity GroupVariableEntity
 end
diff --git a/app/serializers/issuable_entity.rb b/app/serializers/issuable_entity.rb
index 6f31fbd6b7c193973d20c268d0620e0636356dfb..e71bd3313fbb1bb52d50d52faeb1434cf3d18739 100644
--- a/app/serializers/issuable_entity.rb
+++ b/app/serializers/issuable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class IssuableEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/issuable_sidebar_entity.rb b/app/serializers/issuable_sidebar_entity.rb
index 29138c803dfdd986864fb809dc571857f1565588..773d78d324cbcf2d7b60997b4a6c67f204630583 100644
--- a/app/serializers/issuable_sidebar_entity.rb
+++ b/app/serializers/issuable_sidebar_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class IssuableSidebarEntity < Grape::Entity
   include TimeTrackableEntity
   include RequestAwareEntity
diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb
index 840fdbcbf1409446bc567aead151abf9c6077fe8..16a477c92fa08760190425ef3063ccd216311500 100644
--- a/app/serializers/issue_entity.rb
+++ b/app/serializers/issue_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class IssueEntity < IssuableEntity
   include TimeTrackableEntity
 
diff --git a/app/serializers/issue_serializer.rb b/app/serializers/issue_serializer.rb
index 2555595379b553b76d53ad03dddbbaf215809b82..37cf5e28396a8039aec1833180df12de18d9d2fe 100644
--- a/app/serializers/issue_serializer.rb
+++ b/app/serializers/issue_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class IssueSerializer < BaseSerializer
   # This overrided method takes care of which entity should be used
   # to serialize the `issue` based on `basic` key in `opts` param.
diff --git a/app/serializers/issue_sidebar_entity.rb b/app/serializers/issue_sidebar_entity.rb
index 6c823dbfe951b2b5f80dc2671b5378bde7d72d8d..349ad9d1fef42f157b4f13b61c75c4178671e963 100644
--- a/app/serializers/issue_sidebar_entity.rb
+++ b/app/serializers/issue_sidebar_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class IssueSidebarEntity < IssuableSidebarEntity
   expose :assignees, using: API::Entities::UserBasic
 end
diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb
index 960e7291ae62e5ffbf0ca2e81b423430b2074539..7bc1d87dea5a7a3646f87e750bdf3aaf58543b34 100644
--- a/app/serializers/job_entity.rb
+++ b/app/serializers/job_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class JobEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/job_group_entity.rb b/app/serializers/job_group_entity.rb
index 8554de555177e8f3458a4da38d286457f962d89a..0941a9d36be9e43a6147435b1a90bb1f7ec5431e 100644
--- a/app/serializers/job_group_entity.rb
+++ b/app/serializers/job_group_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class JobGroupEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/label_entity.rb b/app/serializers/label_entity.rb
index 4452161051e4c44955c8f13f76e71c1861d8f03b..98743d62b501fa81393e70db5fbe1b7177bb0a2d 100644
--- a/app/serializers/label_entity.rb
+++ b/app/serializers/label_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class LabelEntity < Grape::Entity
   expose :id, if: ->(label, _) { !label.is_a?(GlobalLabel) }
 
diff --git a/app/serializers/label_serializer.rb b/app/serializers/label_serializer.rb
index ad6ba8c46c99e3256a6274d612051eb515a30c63..25b9f7de243d104f476c39871f2c6fc8cae07f25 100644
--- a/app/serializers/label_serializer.rb
+++ b/app/serializers/label_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class LabelSerializer < BaseSerializer
   entity LabelEntity
 
diff --git a/app/serializers/lfs_file_lock_entity.rb b/app/serializers/lfs_file_lock_entity.rb
index 264a77adc3fb13599e9aab9ff8d55c60db77a797..7961c4e666b374492f8c13ae5d14aca2d7d0f760 100644
--- a/app/serializers/lfs_file_lock_entity.rb
+++ b/app/serializers/lfs_file_lock_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class LfsFileLockEntity < Grape::Entity
   root 'locks', 'lock'
 
diff --git a/app/serializers/lfs_file_lock_serializer.rb b/app/serializers/lfs_file_lock_serializer.rb
index ba8fb1a461d068b74f957f3c9f711ff0a6537084..0367097e3764399746330b911a2c95eef67bff98 100644
--- a/app/serializers/lfs_file_lock_serializer.rb
+++ b/app/serializers/lfs_file_lock_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class LfsFileLockSerializer < BaseSerializer
   entity LfsFileLockEntity
 end
diff --git a/app/serializers/merge_request_basic_entity.rb b/app/serializers/merge_request_basic_entity.rb
index 1c06691026df28d4a61590bd9591ba3a47842d9e..f7eb74cf3923fd0b96298dbeb01c491648acd78f 100644
--- a/app/serializers/merge_request_basic_entity.rb
+++ b/app/serializers/merge_request_basic_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MergeRequestBasicEntity < IssuableSidebarEntity
   expose :assignee_id
   expose :merge_status
diff --git a/app/serializers/merge_request_basic_serializer.rb b/app/serializers/merge_request_basic_serializer.rb
index cc5c664c8fa99a467726dee154e616ce318ab039..a68b48b00db3d4e29b5fdbd9972e2b081753d4f9 100644
--- a/app/serializers/merge_request_basic_serializer.rb
+++ b/app/serializers/merge_request_basic_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MergeRequestBasicSerializer < BaseSerializer
   entity MergeRequestBasicEntity
 end
diff --git a/app/serializers/merge_request_create_entity.rb b/app/serializers/merge_request_create_entity.rb
index 11234313293cd4608a71204c068c03e7589ad0fd..e7a93004dda6d735a0151ead699bb8e50c379a7f 100644
--- a/app/serializers/merge_request_create_entity.rb
+++ b/app/serializers/merge_request_create_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MergeRequestCreateEntity < Grape::Entity
   expose :iid
 
diff --git a/app/serializers/merge_request_create_serializer.rb b/app/serializers/merge_request_create_serializer.rb
index 08daf473319fd278a1f3c7b03a16271238c08d2b..b6df9ee13fc9d0d143b918003282f02eb4e9766e 100644
--- a/app/serializers/merge_request_create_serializer.rb
+++ b/app/serializers/merge_request_create_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MergeRequestCreateSerializer < BaseSerializer
   entity MergeRequestCreateEntity
 end
diff --git a/app/serializers/merge_request_diff_entity.rb b/app/serializers/merge_request_diff_entity.rb
index 32c761b45aceb375950eba7e24804857a44ee138..433bfe6047450cdd55b7050563ef06b0f51903cd 100644
--- a/app/serializers/merge_request_diff_entity.rb
+++ b/app/serializers/merge_request_diff_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MergeRequestDiffEntity < Grape::Entity
   include Gitlab::Routing
   include GitHelper
diff --git a/app/serializers/merge_request_metrics_entity.rb b/app/serializers/merge_request_metrics_entity.rb
index 3548107ac168d12e91d45bf3c17283378b33117e..1c9db08d1039346bd2354f81d24be06036541649 100644
--- a/app/serializers/merge_request_metrics_entity.rb
+++ b/app/serializers/merge_request_metrics_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MergeRequestMetricsEntity < Grape::Entity
   expose :latest_closed_at, as: :closed_at
   expose :merged_at
diff --git a/app/serializers/merge_request_serializer.rb b/app/serializers/merge_request_serializer.rb
index caf193bdae37556bad0e51dec256568f085e8302..1f8c830e1aa16bbf04565b740f11900b2cb051b5 100644
--- a/app/serializers/merge_request_serializer.rb
+++ b/app/serializers/merge_request_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MergeRequestSerializer < BaseSerializer
   # This overrided method takes care of which entity should be used
   # to serialize the `merge_request` based on `serializer` key in `opts` param.
diff --git a/app/serializers/merge_request_user_entity.rb b/app/serializers/merge_request_user_entity.rb
index 33fc7b724d5d7a7652695e5cc5504af04b44ac11..fd2d289711346fce62f54103c38a23328f8aa00f 100644
--- a/app/serializers/merge_request_user_entity.rb
+++ b/app/serializers/merge_request_user_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MergeRequestUserEntity < UserEntity
   include RequestAwareEntity
   include BlobHelper
diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb
index a78bd77cf7c8abd828a95088dff6ffe981a10c1f..4fe04e4b206e99f9414a79322b58e29a7ec67339 100644
--- a/app/serializers/merge_request_widget_entity.rb
+++ b/app/serializers/merge_request_widget_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MergeRequestWidgetEntity < IssuableEntity
   expose :state
   expose :in_progress_merge_commit_sha
diff --git a/app/serializers/note_attachment_entity.rb b/app/serializers/note_attachment_entity.rb
index 1ad50568ab96c2369c779cea0a7c5119cf54bd9e..dc801e2bf4e936eb1e92efe17e75c44a1d839bd1 100644
--- a/app/serializers/note_attachment_entity.rb
+++ b/app/serializers/note_attachment_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class NoteAttachmentEntity < Grape::Entity
   expose :url
   expose :filename
diff --git a/app/serializers/note_entity.rb b/app/serializers/note_entity.rb
index 0e1f94a9f61ab0bb25527d178426cb9642639fb3..daa5c24d0f5126a98e7e9a833e945b3d599efe2b 100644
--- a/app/serializers/note_entity.rb
+++ b/app/serializers/note_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class NoteEntity < API::Entities::Note
   include RequestAwareEntity
   include NotesHelper
diff --git a/app/serializers/note_user_entity.rb b/app/serializers/note_user_entity.rb
index 7289f3a022257fb8e420e8e51742cdc04fc87de9..b00dfa7d353ccebf349a19b26b6fc93c3d811768 100644
--- a/app/serializers/note_user_entity.rb
+++ b/app/serializers/note_user_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class NoteUserEntity < UserEntity
   unexpose :web_url
 end
diff --git a/app/serializers/pipeline_details_entity.rb b/app/serializers/pipeline_details_entity.rb
index 8ba9cac53c4c5894f3caa701948bbdaa7c852bfe..3b56767f774f797d8411c6fd1e2e298c731d97a4 100644
--- a/app/serializers/pipeline_details_entity.rb
+++ b/app/serializers/pipeline_details_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class PipelineDetailsEntity < PipelineEntity
   expose :details do
     expose :ordered_stages, as: :stages, using: StageEntity
diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb
index f782b411b8405b6efeeb0632e7c77b1793614378..6cf1925adda6c17f30bcc8c251409b3f4f0ef5a3 100644
--- a/app/serializers/pipeline_entity.rb
+++ b/app/serializers/pipeline_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class PipelineEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index 17a022539bb39b9afc787730268c80e5919fca0e..4a33160afa1a3a033dbf66400624d8b0a26c8180 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class PipelineSerializer < BaseSerializer
   include WithPagination
   entity PipelineDetailsEntity
diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb
index b3e5fd21e971c3e6eeadc3c791c21e9a9aa10e85..60c4ba135d6b4b4c580d8526d0c1c463be4269e0 100644
--- a/app/serializers/project_entity.rb
+++ b/app/serializers/project_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/project_mirror_entity.rb b/app/serializers/project_mirror_entity.rb
index a9c08ac021ad3b4940373790937b22023560db0a..8aba244cd11bb659681a573bae9c0ed900e7ff49 100644
--- a/app/serializers/project_mirror_entity.rb
+++ b/app/serializers/project_mirror_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectMirrorEntity < Grape::Entity
   expose :id
 
diff --git a/app/serializers/project_note_entity.rb b/app/serializers/project_note_entity.rb
index e541bfbee8d11bc6fb4c5af678ad79c06d3702d6..d7c4d0aacc68075c97acd714e5639eba7977b0c4 100644
--- a/app/serializers/project_note_entity.rb
+++ b/app/serializers/project_note_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectNoteEntity < NoteEntity
   expose :human_access do |note|
     note.project.team.human_max_access(note.author_id)
diff --git a/app/serializers/project_note_serializer.rb b/app/serializers/project_note_serializer.rb
index 763ad0bdb3fab85f167044f907e2b9b267d8895e..2182904e81521c71d5271fff8f126929d5248e37 100644
--- a/app/serializers/project_note_serializer.rb
+++ b/app/serializers/project_note_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectNoteSerializer < BaseSerializer
   entity ProjectNoteEntity
 end
diff --git a/app/serializers/project_serializer.rb b/app/serializers/project_serializer.rb
index 74de1e79a8f832f6bc9d2373ea04c823b307b00e..23b96c2fc9e2397d6a77f377a56b8b96bee197be 100644
--- a/app/serializers/project_serializer.rb
+++ b/app/serializers/project_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ProjectSerializer < BaseSerializer
   entity ProjectEntity
 end
diff --git a/app/serializers/request_aware_entity.rb b/app/serializers/request_aware_entity.rb
index d53fcfb8c1b3d0b2e832ed7f62c8e5bede07ca81..1524c1291d83fe9008d8f70477057e9baf00b1be 100644
--- a/app/serializers/request_aware_entity.rb
+++ b/app/serializers/request_aware_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module RequestAwareEntity
   extend ActiveSupport::Concern
 
diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb
index db26eadab2de02843c7e575c18715d9613992e47..04ec80e0efacafe79a45281a331945b1b42042d4 100644
--- a/app/serializers/runner_entity.rb
+++ b/app/serializers/runner_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class RunnerEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb
index 2516df70ad916c84a5ca6194260eb5971907b102..00e6d32ee3a9b21dd61e9e3ce04326e2aa7c2daa 100644
--- a/app/serializers/stage_entity.rb
+++ b/app/serializers/stage_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class StageEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/stage_serializer.rb b/app/serializers/stage_serializer.rb
index 091d8e91e433dabe6ec18491e987ba4c1224593e..11fb0d3f852ba0dd9ccd171021c6e5476fb83f34 100644
--- a/app/serializers/stage_serializer.rb
+++ b/app/serializers/stage_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class StageSerializer < BaseSerializer
   include WithPagination
 
diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb
index 47df7f9dcf9012a3fae5e29145c0a5d9abba6856..306c30f0323b8529d6603274cfa095b440271989 100644
--- a/app/serializers/status_entity.rb
+++ b/app/serializers/status_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class StatusEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/submodule_entity.rb b/app/serializers/submodule_entity.rb
index ed1f1ae0ef07e35075728f8b060db5be2d1a1817..e475a4f301faf67196025b2066cabe87e187a9da 100644
--- a/app/serializers/submodule_entity.rb
+++ b/app/serializers/submodule_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class SubmoduleEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/time_trackable_entity.rb b/app/serializers/time_trackable_entity.rb
index e81cd7bec72aecf9938996c68308eeb0145abdc0..613d19703a4f0365e343a534da8a225966993a38 100644
--- a/app/serializers/time_trackable_entity.rb
+++ b/app/serializers/time_trackable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module TimeTrackableEntity
   extend ActiveSupport::Concern
   extend Grape
diff --git a/app/serializers/tree_entity.rb b/app/serializers/tree_entity.rb
index 9f1b485347fbfccf35d3c53b19f875a0bac3b42e..9b7dc80e1d9dd5e92eb65b4233929b8ea52fed6e 100644
--- a/app/serializers/tree_entity.rb
+++ b/app/serializers/tree_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class TreeEntity < Grape::Entity
   include RequestAwareEntity
 
diff --git a/app/serializers/tree_root_entity.rb b/app/serializers/tree_root_entity.rb
index 496f070ddbd658f1f05e25e5cc7494535891643a..f1cfcd943d8a8699a59231727fa79a00990972e7 100644
--- a/app/serializers/tree_root_entity.rb
+++ b/app/serializers/tree_root_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # TODO: Inherit from TreeEntity, when `Tree` implements `id` and `name` like `Gitlab::Git::Tree`.
 class TreeRootEntity < Grape::Entity
   include RequestAwareEntity
diff --git a/app/serializers/tree_serializer.rb b/app/serializers/tree_serializer.rb
index 713ade23bc9c38c68ce6abd82e7f8d18c765b05e..536b8ab1ae2adec33aabfbc4f665891b8e7e7471 100644
--- a/app/serializers/tree_serializer.rb
+++ b/app/serializers/tree_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class TreeSerializer < BaseSerializer
   entity TreeRootEntity
 end
diff --git a/app/serializers/user_entity.rb b/app/serializers/user_entity.rb
index 876512b12dc6c16db081f22e86039f29c1b8b46d..6236d66ff4a18f5703fde952924361d382b36167 100644
--- a/app/serializers/user_entity.rb
+++ b/app/serializers/user_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class UserEntity < API::Entities::UserBasic
   include RequestAwareEntity
 
diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb
index 49a71ebac614eb7c34caf612bdbd5905d667e137..2111e1b5667fd801d0d46f653fd66ad1ae25c647 100644
--- a/app/serializers/user_serializer.rb
+++ b/app/serializers/user_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class UserSerializer < BaseSerializer
   entity UserEntity
 end
diff --git a/app/serializers/variable_entity.rb b/app/serializers/variable_entity.rb
index d576745c07320129187f812506f8cb79275e8047..85cf367fe510399c5fceb220a96d977e2e8864f0 100644
--- a/app/serializers/variable_entity.rb
+++ b/app/serializers/variable_entity.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class VariableEntity < Grape::Entity
   expose :id
   expose :key
diff --git a/app/serializers/variable_serializer.rb b/app/serializers/variable_serializer.rb
index 32ae82ab51c8d981911fb3b39923ddc553b4def8..586666cad8e24e5b0abebd0937d02e585f8f5a0e 100644
--- a/app/serializers/variable_serializer.rb
+++ b/app/serializers/variable_serializer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class VariableSerializer < BaseSerializer
   entity VariableEntity
 end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index e560d40371ef0a42133abc5f3406a1a534153167..0c426faa22de986506f0036111f8483a4daa6f46 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -25,7 +25,7 @@ module Projects
 
       success
     rescue => e
-      error("Error importing repository #{project.import_url} into #{project.full_path} - #{e.message}")
+      error("Error importing repository #{project.safe_import_url} into #{project.full_path} - #{e.message}")
     end
 
     private
diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb
index c69b46cab5a58673ed0d2e6f42a1ddd2c641173d..acc2fa153ae8336ac17b9d6e6676a041b704bae0 100644
--- a/app/services/users/build_service.rb
+++ b/app/services/users/build_service.rb
@@ -64,7 +64,8 @@ module Users
         :theme_id,
         :twitter,
         :username,
-        :website_url
+        :website_url,
+        :private_profile
       ]
     end
 
diff --git a/app/uploaders/import_export_uploader.rb b/app/uploaders/import_export_uploader.rb
index 213ac5c801197e49a276ff9f4c97657409f4b8a5..7c45ba5ca95f0921e637946ac2a6803e4616894a 100644
--- a/app/uploaders/import_export_uploader.rb
+++ b/app/uploaders/import_export_uploader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class ImportExportUploader < AttachmentUploader
   EXTENSION_WHITELIST = %w[tar.gz].freeze
 
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 18f2c1a509f78bdbb711a388f03d9cc721fb3079..fac61f9d2494567a505d06b12cd71190e90f566b 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -91,10 +91,10 @@
               %span.light.float-right
                 = boolean_to_icon gravatar_enabled?
             - omniauth = "OmniAuth"
-            %p{ "aria-label" => "#{omniauth}: status " + (Gitlab.config.omniauth.enabled ? "on" : "off") }
+            %p{ "aria-label" => "#{omniauth}: status " + (Gitlab::Auth.omniauth_enabled? ? "on" : "off") }
               = omniauth
               %span.light.float-right
-                = boolean_to_icon Gitlab.config.omniauth.enabled
+                = boolean_to_icon Gitlab::Auth.omniauth_enabled?
             - reply_email = "Reply by email"
             %p{ "aria-label" => "#{reply_email}: status " + (Gitlab::IncomingEmail.enabled? ? "on" : "off") }
               = reply_email
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index 62b7a4cbd07aabe2813b96676e042721771bafea..62be38e9dd2f8bdd1547fb4376770b4eaeadaeea 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -11,7 +11,6 @@
 
 - add_to_breadcrumbs _("Runners"), admin_runners_path
 - breadcrumb_title "##{@runner.id}"
-- @no_container = true
 
 - if @runner.instance_type?
   .bs-callout.bs-callout-success
diff --git a/app/views/import/bitbucket_server/new.html.haml b/app/views/import/bitbucket_server/new.html.haml
index 11bd53516a698cf1dbd225150a0e1bbf1e7e8ab4..12e0b917de572f33734db0bd5aac8f9ff2ff6655 100644
--- a/app/views/import/bitbucket_server/new.html.haml
+++ b/app/views/import/bitbucket_server/new.html.haml
@@ -13,7 +13,7 @@
   .form-group.row
     = label_tag :bitbucket_server_url, 'Bitbucket Server URL', class: 'col-form-label col-md-2'
     .col-md-4
-      = text_field_tag :bitbucket_server_url, '', class: 'form-control append-right-8', placeholder: _('URL for Bitbucket Server'), size: 40
+      = text_field_tag :bitbucket_server_url, '', class: 'form-control append-right-8', placeholder: _('https://your-bitbucket-server'), size: 40
   .form-group.row
     = label_tag :bitbucket_server_url, 'Username', class: 'col-form-label col-md-2'
     .col-md-4
diff --git a/app/views/layouts/_recaptcha_verification.html.haml b/app/views/layouts/_recaptcha_verification.html.haml
index e6f87ddd383294e3873e2544a4b2dc8651971ef5..3405fb9d5eff309d36a582e3035e7c031f799dc2 100644
--- a/app/views/layouts/_recaptcha_verification.html.haml
+++ b/app/views/layouts/_recaptcha_verification.html.haml
@@ -1,10 +1,10 @@
 - humanized_resource_name = spammable.class.model_name.human.downcase
 
 %h3.page-title
-  Anti-spam verification
+  = _('Anti-spam verification')
 %hr
 
 %p
-  #{"We detected potential spam in the #{humanized_resource_name}. Please solve the reCAPTCHA to proceed."}
+  = _("We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed.") % { humanized_resource_name: humanized_resource_name }
 
 = render 'shared/recaptcha_form', spammable: spammable
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 91f796adb2ea6b8e3d489af37c2cceab37432494..556ad8cf306a74314d731012802b73e20b5bd692 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -1,7 +1,7 @@
 - if controller.controller_path =~ /^groups/ && @group.persisted?
-  - label = 'This group'
+  - label = _('This group')
 - if controller.controller_path =~ /^projects/ && @project.persisted?
-  - label = 'This project'
+  - label = _('This project')
 - if @group && @group.persisted? && @group.path
   - group_data_attrs = { group_path: j(@group.path), name: @group.name, issues_path: issues_group_path(j(@group.path)), mr_path: merge_requests_group_path(j(@group.path)) }
 - if @project && @project.persisted?
@@ -13,21 +13,21 @@
         .location-badge= label
       .search-input-wrap
         .dropdown{ data: { url: search_autocomplete_path } }
-          = search_field_tag 'search', nil, placeholder: 'Search',
+          = search_field_tag 'search', nil, placeholder: _('Search'),
                                             class: 'search-input dropdown-menu-toggle no-outline js-search-dashboard-options',
                                             spellcheck: false,
                                             tabindex: '1',
                                             autocomplete: 'off',
                                             data: { issues_path: issues_dashboard_path,
                                               mr_path: merge_requests_dashboard_path },
-                                            aria: { label: 'Search' }
+                                            aria: { label: _('Search') }
           %button.hidden.js-dropdown-search-toggle{ type: 'button', data: { toggle: 'dropdown' } }
           .dropdown-menu.dropdown-select
             = dropdown_content do
               %ul
                 %li.dropdown-menu-empty-item
                   %a
-                    Loading...
+                    = _('Loading...')
             = dropdown_loading
           = sprite_icon('search', size: 16, css_class: 'search-icon')
           = sprite_icon('close', size: 16, css_class: 'clear-icon js-clear-input')
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index 8595157a9971d71114f0af3e0a8e40dadb33a412..31259b8ac258688f143c078e24caec896f86a2f7 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,5 +1,5 @@
-- page_title    "Admin Area"
-- header_title  "Admin Area", admin_root_path
+- page_title    _("Admin Area")
+- header_title  _("Admin Area"), admin_root_path
 - nav "admin"
 - @left_sidebar = true
 
diff --git a/app/views/layouts/dashboard.html.haml b/app/views/layouts/dashboard.html.haml
index cb96bcc2cf4db75c8ab2b8684980acbf62ef6a9f..489ef245a4d0f8478e2f0bbdc5f257d811d4a2c4 100644
--- a/app/views/layouts/dashboard.html.haml
+++ b/app/views/layouts/dashboard.html.haml
@@ -1,5 +1,5 @@
-- page_title    "Dashboard"
-- header_title  "Dashboard", root_path unless header_title
+- page_title    _("Dashboard")
+- header_title  _("Dashboard"), root_path unless header_title
 - sidebar       "dashboard"
 
 = render template: "layouts/application"
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 81f356155550e5fb6d0f3908c545792fa5fe1d14..43bd07679ba8657e0b30f64a1653c727040706c8 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -17,12 +17,11 @@
               - if current_appearance&.description?
                 = brand_text
               - else
-                %h3 Open source software to collaborate on code
+                %h3
+                  = _('Open source software to collaborate on code')
 
                 %p
-                  Manage Git repositories with fine-grained access controls that keep your code secure.
-                  Perform code reviews and enhance collaboration with merge requests.
-                  Each project can also have an issue tracker and a wiki.
+                  = _('Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki.')
 
               - if Gitlab::CurrentSettings.sign_in_text.present?
                 = markdown_field(Gitlab::CurrentSettings.current_application_settings, :sign_in_text)
@@ -32,6 +31,6 @@
       %hr.footer-fixed
       .container.footer-container
         .footer-links
-          = link_to "Explore", explore_root_path
-          = link_to "Help", help_path
-          = link_to "About GitLab", "https://about.gitlab.com/"
+          = link_to _("Explore"), explore_root_path
+          = link_to _("Help"), help_path
+          = link_to _("About GitLab"), "https://about.gitlab.com/"
diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml
index 52805e0da739ab527095099aab5e5c9618f085d0..663e5b24368f248d56684cc9d944ae105cdbb83b 100644
--- a/app/views/layouts/devise_empty.html.haml
+++ b/app/views/layouts/devise_empty.html.haml
@@ -12,6 +12,6 @@
     %hr
     .container
       .footer-links
-        = link_to "Explore", explore_root_path
-        = link_to "Help", help_path
-        = link_to "About GitLab", "https://about.gitlab.com/"
+        = link_to _("Explore"), explore_root_path
+        = link_to _("Help"), help_path
+        = link_to _("About GitLab"), "https://about.gitlab.com/"
diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml
index df65792be735a769a850a2ffe993855e1f07113d..2ab9e55441b785ead78efdadf185e60e7b5c6c23 100644
--- a/app/views/layouts/explore.html.haml
+++ b/app/views/layouts/explore.html.haml
@@ -1,5 +1,5 @@
-- page_title    "Explore"
+- page_title    = _("Explore")
 - unless current_user
-  - header_title  "Explore GitLab", explore_root_path
+  - header_title  = _("Explore GitLab"), explore_root_path
 
 = render template: "layouts/application"
diff --git a/app/views/layouts/group_settings.html.haml b/app/views/layouts/group_settings.html.haml
index 66b115e36de69e0d332051f3965bfbeb9a3b53fa..14c5f0ce04c40f0d7f60a6fe6d0e730271defd72 100644
--- a/app/views/layouts/group_settings.html.haml
+++ b/app/views/layouts/group_settings.html.haml
@@ -1,4 +1,4 @@
-- page_title    "Settings"
+- page_title    = _("Settings")
 - nav           "group"
 
 = render template: "layouts/group"
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 3aa8eb18bf33b2ae9dd6572f9bb12630513700d1..97c04dda8cb7494ffd3700bd88904d171a0b3ed1 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -4,7 +4,7 @@
     .header-content
       .title-container
         %h1.title
-          = link_to root_path, title: 'Dashboard', id: 'logo' do
+          = link_to root_path, title: _('Dashboard'), id: 'logo' do
             = brand_header_logo
             - logo_text = brand_header_logo_type
             - if logo_text.present?
@@ -24,26 +24,26 @@
             %li.nav-item.d-none.d-sm-none.d-md-block.m-auto
               = render 'layouts/search' unless current_controller?(:search)
             %li.nav-item.d-inline-block.d-sm-none.d-md-none
-              = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+              = link_to search_path, title: _('Search'), aria: { label: _("Search") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
                 = sprite_icon('search', size: 16)
 
           - if header_link?(:issues)
             = nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do
-              = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+              = link_to assigned_issues_dashboard_path, title: _('Issues'), class: 'dashboard-shortcuts-issues', aria: { label: _("Issues") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
                 = sprite_icon('issues', size: 16)
                 - issues_count = assigned_issuables_count(:issues)
                 %span.badge.badge-pill.issues-count{ class: ('hidden' if issues_count.zero?) }
                   = number_with_delimiter(issues_count)
           - if header_link?(:merge_requests)
             = nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter" }) do
-              = link_to assigned_mrs_dashboard_path, title: 'Merge requests', class: 'dashboard-shortcuts-merge_requests', aria: { label: "Merge requests" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+              = link_to assigned_mrs_dashboard_path, title: _('Merge requests'), class: 'dashboard-shortcuts-merge_requests', aria: { label: _("Merge requests") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
                 = sprite_icon('git-merge', size: 16)
                 - merge_requests_count = assigned_issuables_count(:merge_requests)
                 %span.badge.badge-pill.merge-requests-count{ class: ('hidden' if merge_requests_count.zero?) }
                   = number_with_delimiter(merge_requests_count)
           - if header_link?(:todos)
             = nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
-              = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+              = link_to dashboard_todos_path, title: _('Todos'), aria: { label: _("Todos") }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
                 = sprite_icon('todo-done', size: 16)
                 %span.badge.badge-pill.todos-count{ class: ('hidden' if todos_pending_count.zero?) }
                   = todos_count_format(todos_pending_count)
@@ -56,16 +56,16 @@
                 = render 'layouts/header/current_user_dropdown'
           - if header_link?(:admin_impersonation)
             %li.nav-item.impersonation
-              = link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: "Stop impersonation", aria: { label: 'Stop impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
+              = link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: _("Stop impersonation"), aria: { label: _('Stop impersonation') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
                 = icon('user-secret')
           - if header_link?(:sign_in)
             %li.nav-item
               %div
-                - sign_in_text = allow_signup? ? 'Sign in / Register' : 'Sign in'
+                - sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in')
                 = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
 
 
       %button.navbar-toggler.d-block.d-sm-none{ type: 'button' }
-        %span.sr-only Toggle navigation
+        %span.sr-only= _("Toggle navigation")
         = sprite_icon('more', size: 12, css_class: 'more-icon js-navbar-toggle-right')
         = sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left')
diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml
index 792291bde7519fa9cbd22704ecbf02d8ebe99d94..e134f416c708c2901d24c98140eee4c723e501d6 100644
--- a/app/views/layouts/header/_new_dropdown.haml
+++ b/app/views/layouts/header/_new_dropdown.haml
@@ -1,5 +1,5 @@
 %li.header-new.dropdown
-  = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do
+  = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: _("New..."), ref: 'tooltip', aria: { label: _("New...") }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do
     = sprite_icon('plus-square', size: 16)
     = sprite_icon('angle-down', css_class: 'caret-down')
   .dropdown-menu.dropdown-menu-right
@@ -8,13 +8,14 @@
         - create_group_project = can?(current_user, :create_projects, @group)
         - create_group_subgroup = can?(current_user, :create_subgroup, @group)
         - if create_group_project || create_group_subgroup
-          %li.dropdown-bold-header This group
+          %li.dropdown-bold-header
+            = _('This group')
           - if create_group_project
             %li.header-new-group-project
-              = link_to 'New project', new_project_path(namespace_id: @group.id)
+              = link_to _('New project'), new_project_path(namespace_id: @group.id)
           - if create_group_subgroup
             %li
-              = link_to 'New subgroup', new_group_path(parent_id: @group.id)
+              = link_to _('New subgroup'), new_group_path(parent_id: @group.id)
           %li.divider
           %li.dropdown-bold-header GitLab
 
@@ -23,23 +24,24 @@
         - merge_project = merge_request_source_project_for_project(@project)
         - create_project_snippet = can?(current_user, :create_project_snippet, @project)
         - if create_project_issue || merge_project || create_project_snippet
-          %li.dropdown-bold-header This project
+          %li.dropdown-bold-header
+            = _('This project')
           - if create_project_issue
             %li
-              = link_to 'New issue', new_project_issue_path(@project)
+              = link_to _('New issue'), new_project_issue_path(@project)
           - if merge_project
             %li
-              = link_to 'New merge request', project_new_merge_request_path(merge_project)
+              = link_to _('New merge request'), project_new_merge_request_path(merge_project)
           - if create_project_snippet
             %li.header-new-project-snippet
-              = link_to 'New snippet', new_project_snippet_path(@project)
+              = link_to _('New snippet'), new_project_snippet_path(@project)
           %li.divider
           %li.dropdown-bold-header GitLab
       - if current_user.can_create_project?
         %li
-          = link_to 'New project', new_project_path, class: 'qa-global-new-project-link'
+          = link_to _('New project'), new_project_path, class: 'qa-global-new-project-link'
       - if current_user.can_create_group?
         %li
-          = link_to 'New group', new_group_path
+          = link_to _('New group'), new_group_path
       %li
-        = link_to 'New snippet', new_snippet_path
+        = link_to _('New snippet'), new_snippet_path
diff --git a/app/views/layouts/help.html.haml b/app/views/layouts/help.html.haml
index 78927bfffcd27efc910a9cd558ed06b46192c650..a913bea0c93f366e78048cacb33cf2dbd80d7fb4 100644
--- a/app/views/layouts/help.html.haml
+++ b/app/views/layouts/help.html.haml
@@ -1,5 +1,5 @@
-- @breadcrumb_title = "Help"
-- page_title    "Help"
-- header_title  "Help", help_path
+- @breadcrumb_title = _("Help")
+- page_title    _("Help")
+- header_title  _("Help"), help_path
 
 = render template: "layouts/application"
diff --git a/app/views/layouts/koding.html.haml b/app/views/layouts/koding.html.haml
index 22319bba745f84a8c597eab946aca8a455c2a1f9..45ccd38f687067361c11d8703bc07400a28ee717 100644
--- a/app/views/layouts/koding.html.haml
+++ b/app/views/layouts/koding.html.haml
@@ -1,5 +1,5 @@
-- page_title        "Koding"
-- page_description  "Koding Dashboard"
-- header_title      "Koding", koding_path
+- page_title        _("Koding")
+- page_description  _("Koding Dashboard")
+- header_title      _("Koding"), koding_path
 
 = render template: "layouts/application"
diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb
index 8e20c4a4b2ac01aa9f20ded0fb09e1e321b1f7a1..8e11174f8d761a46a345909cda3a8b3ccedfcb2f 100644
--- a/app/views/layouts/mailer.text.erb
+++ b/app/views/layouts/mailer.text.erb
@@ -1,4 +1,4 @@
 <%= yield -%>
 
 -- <%# signature marker %>
-You're receiving this email because of your account on <%= Gitlab.config.gitlab.host %>.
+<%= _("You're receiving this email because of your account on %{host}.") % { host: Gitlab.config.gitlab.host } %>
diff --git a/app/views/layouts/mailer/devise.html.haml b/app/views/layouts/mailer/devise.html.haml
index 054b2c2fa262edff35d009c9ddbedf4de30ce9d4..beaaaa5cd682517a13d4a58ef059fc84b4f20043 100644
--- a/app/views/layouts/mailer/devise.html.haml
+++ b/app/views/layouts/mailer/devise.html.haml
@@ -3,17 +3,17 @@
     %tr
       %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:13px;line-height:1.6;color:#5c5c5c;" }
         %div
-          Everyone can contribute
+          = _('Everyone can contribute')
         %div
-          = link_to 'Blog', 'https://about.gitlab.com/blog/', style: "color:#3777b0;text-decoration:none;"
+          = link_to _('Blog'), 'https://about.gitlab.com/blog/', style: "color:#3777b0;text-decoration:none;"
           &middot;
-          = link_to 'Twitter', 'https://twitter.com/gitlab', style: "color:#3777b0;text-decoration:none;"
+          = link_to _('Twitter'), 'https://twitter.com/gitlab', style: "color:#3777b0;text-decoration:none;"
           &middot;
-          = link_to 'Facebook', 'https://www.facebook.com/gitlab/', style: "color:#3777b0;text-decoration:none;"
+          = link_to _('Facebook'), 'https://www.facebook.com/gitlab/', style: "color:#3777b0;text-decoration:none;"
           &middot;
-          = link_to 'YouTube', 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg', style: "color:#3777b0;text-decoration:none;"
+          = link_to _('YouTube'), 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg', style: "color:#3777b0;text-decoration:none;"
           &middot;
-          = link_to 'LinkedIn', 'https://www.linkedin.com/company/gitlab-com', style: "color:#3777b0;text-decoration:none;"
+          = link_to _('LinkedIn'), 'https://www.linkedin.com/company/gitlab-com', style: "color:#3777b0;text-decoration:none;"
 
 = render layout: 'layouts/mailer' do
   %tr
diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml
index 002922e13f12d48daa2d230f23a15dc1396582f4..f53bd2b5e4ddacacb4f108cecce1f1cdc416a85f 100644
--- a/app/views/layouts/nav/_breadcrumbs.html.haml
+++ b/app/views/layouts/nav/_breadcrumbs.html.haml
@@ -5,7 +5,7 @@
   .breadcrumbs-container
     - if defined?(@left_sidebar)
       = button_tag class: 'toggle-mobile-nav', type: 'button' do
-        %span.sr-only Open sidebar
+        %span.sr-only= _("Open sidebar")
         = icon ('bars')
     .breadcrumbs-links.js-title-container
       %ul.list-unstyled.breadcrumbs-list.js-breadcrumbs-list
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 4029287fc0eb21bd76b23fcc44e2bb8c235c35bf..a71a4b13a7eac377b34545a305a15f200b2eb13b 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -2,7 +2,7 @@
   - if dashboard_nav_link?(:projects)
     = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects qa-projects-dropdown" }) do
       %button{ type: 'button', data: { toggle: "dropdown" } }
-        Projects
+        = _('Projects')
         = sprite_icon('angle-down', css_class: 'caret-down')
       .dropdown-menu.frequent-items-dropdown-menu
         = render "layouts/nav/projects_dropdown/show"
@@ -10,68 +10,68 @@
   - if dashboard_nav_link?(:groups)
     = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { id: 'nav-groups-dropdown', class: "home dropdown header-groups qa-groups-dropdown" }) do
       %button{ type: 'button', data: { toggle: "dropdown" } }
-        Groups
+        = _('Groups')
         = sprite_icon('angle-down', css_class: 'caret-down')
       .dropdown-menu.frequent-items-dropdown-menu
         = render "layouts/nav/groups_dropdown/show"
 
   - if dashboard_nav_link?(:activity)
     = nav_link(path: 'dashboard#activity', html_options: { class: "d-none d-lg-block d-xl-block" }) do
-      = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
-        Activity
+      = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: _('Activity') do
+        = _('Activity')
 
   - if dashboard_nav_link?(:milestones)
     = nav_link(controller: 'dashboard/milestones', html_options: { class: "d-none d-lg-block d-xl-block" }) do
-      = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do
-        Milestones
+      = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: _('Milestones') do
+        = _('Milestones')
 
   - if dashboard_nav_link?(:snippets)
     = nav_link(controller: 'dashboard/snippets', html_options: { class: "d-none d-lg-block d-xl-block" }) do
-      = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
-        Snippets
+      = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: _('Snippets') do
+        = _('Snippets')
 
   - if any_dashboard_nav_link?([:groups, :milestones, :activity, :snippets])
     %li.header-more.dropdown.d-lg-none.d-xl-none
       %a{ href: "#", data: { toggle: "dropdown" } }
-        More
+        = _('More')
         = sprite_icon('angle-down', css_class: 'caret-down')
       .dropdown-menu
         %ul
           - if dashboard_nav_link?(:activity)
             = nav_link(path: 'dashboard#activity') do
-              = link_to activity_dashboard_path, title: 'Activity' do
-                Activity
+              = link_to activity_dashboard_path, title: _('Activity') do
+                = _('Activity')
 
           - if dashboard_nav_link?(:milestones)
             = nav_link(controller: 'dashboard/milestones') do
-              = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do
-                Milestones
+              = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: _('Milestones') do
+                = _('Milestones')
 
           - if dashboard_nav_link?(:snippets)
             = nav_link(controller: 'dashboard/snippets') do
-              = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
-                Snippets
+              = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: _('Snippets') do
+                = _('Snippets')
 
   -# Shortcut to Dashboard > Projects
   - if dashboard_nav_link?(:projects)
     %li.hidden
-      = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
-        Projects
+      = link_to dashboard_projects_path, title: _('Projects'), class: 'dashboard-shortcuts-projects' do
+        = _('Projects')
 
   - if current_controller?('ide')
     %li.line-separator.d-none.d-sm-block
       = nav_link(controller: 'ide') do
-        = link_to '#', class: 'dashboard-shortcuts-web-ide', title: 'Web IDE' do
-          Web IDE
+        = link_to '#', class: 'dashboard-shortcuts-web-ide', title: _('Web IDE') do
+          = _('Web IDE')
 
   - if current_user.admin? || Gitlab::Sherlock.enabled?
     %li.line-separator.d-none.d-sm-block
   - if current_user.admin?
     = nav_link(controller: 'admin/dashboard') do
-      = link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: 'Admin area', aria: { label: "Admin area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+      = link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: _('Admin area'), aria: { label: _("Admin area") }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
         = sprite_icon('admin', size: 18)
   - if Gitlab::Sherlock.enabled?
     %li
-      = link_to sherlock_transactions_path, class: 'admin-icon', title: 'Sherlock Transactions',
+      = link_to sherlock_transactions_path, class: 'admin-icon', title: _('Sherlock Transactions'),
         data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
         = icon('tachometer fw')
diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml
index 50bde9d1754d95a0613883c7218e52b3e4e1050c..7d18cd8978b1b3ab275a17b5e6e314e01dd7d9a9 100644
--- a/app/views/layouts/nav/_explore.html.haml
+++ b/app/views/layouts/nav/_explore.html.haml
@@ -1,15 +1,15 @@
 %ul.list-unstyled.navbar-sub-nav
   - if explore_nav_link?(:projects)
     = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
-      = link_to explore_root_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
-        Projects
+      = link_to explore_root_path, title: _('Projects'), class: 'dashboard-shortcuts-projects' do
+        = _('Projects')
   - if explore_nav_link?(:groups)
     = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
-      = link_to explore_groups_path, title: 'Groups', class: 'dashboard-shortcuts-groups' do
-        Groups
+      = link_to explore_groups_path, title: _('Groups'), class: 'dashboard-shortcuts-groups' do
+        = _('Groups')
   - if explore_nav_link?(:snippets)
     = nav_link(controller: :snippets) do
-      = link_to explore_snippets_path, title: 'Snippets', class: 'dashboard-shortcuts-snippets' do
-        Snippets
+      = link_to explore_snippets_path, title: _('Snippets'), class: 'dashboard-shortcuts-snippets' do
+        = _('Snippets')
   %li
-    = link_to "Help", help_path, title: 'About GitLab CE'
+    = link_to _("Help"), help_path, title: _('About GitLab CE')
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 4c73da4c75bb1e9e0751592f332ae7cd04897c1d..0047efa363d2a23ce706d0d29964a21bb6862183 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -1,142 +1,143 @@
 .nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
   .nav-sidebar-inner-scroll
     .context-header
-      = link_to admin_root_path, title: 'Admin Overview' do
+      = link_to admin_root_path, title: _('Admin Overview') do
         .avatar-container.s40.settings-avatar
           = sprite_icon('admin', size: 24)
-        .sidebar-context-title Admin Area
+        .sidebar-context-title
+          = _('Admin Area')
     %ul.sidebar-top-level-items
       = nav_link(controller: %w(dashboard admin projects users groups jobs runners gitaly_servers cohorts conversational_development_index), html_options: {class: 'home'}) do
         = link_to admin_root_path, class: 'shortcuts-tree' do
           .nav-icon-container
             = sprite_icon('overview')
           %span.nav-item-name
-            Overview
+            = _('Overview')
         %ul.sidebar-sub-level-items
           = nav_link(controller: %w(dashboard admin projects users groups jobs runners gitaly_servers cohorts conversational_development_index), html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_root_path do
               %strong.fly-out-top-item-name
-                #{ _('Overview') }
+                = _('Overview')
           %li.divider.fly-out-top-item
           = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
-            = link_to admin_root_path, title: 'Overview' do
+            = link_to admin_root_path, title: _('Overview') do
               %span
-                Dashboard
+                = _('Dashboard')
           = nav_link(controller: [:admin, :projects]) do
-            = link_to admin_projects_path, title: 'Projects' do
+            = link_to admin_projects_path, title: _('Projects') do
               %span
-                Projects
+                = _('Projects')
           = nav_link(controller: :users) do
-            = link_to admin_users_path, title: 'Users' do
+            = link_to admin_users_path, title: _('Users') do
               %span
-                Users
+                = _('Users')
           = nav_link(controller: :groups) do
-            = link_to admin_groups_path, title: 'Groups' do
+            = link_to admin_groups_path, title: _('Groups') do
               %span
-                Groups
+                = _('Groups')
           = nav_link path: 'jobs#index' do
-            = link_to admin_jobs_path, title: 'Jobs' do
+            = link_to admin_jobs_path, title: _('Jobs') do
               %span
-                Jobs
+                = _('Jobs')
           = nav_link path: ['runners#index', 'runners#show'] do
-            = link_to admin_runners_path, title: 'Runners' do
+            = link_to admin_runners_path, title: _('Runners') do
               %span
-                Runners
+                = _('Runners')
           = nav_link(controller: :gitaly_servers) do
             = link_to admin_gitaly_servers_path, title: 'Gitaly Servers' do
               %span
-                Gitaly Servers
+                = _('Gitaly Servers')
           = nav_link path: 'cohorts#index' do
-            = link_to admin_cohorts_path, title: 'Cohorts' do
+            = link_to admin_cohorts_path, title: _('Cohorts') do
               %span
-                Cohorts
+                = _('Cohorts')
           = nav_link(controller: :conversational_development_index) do
-            = link_to admin_conversational_development_index_path, title: 'ConvDev Index' do
+            = link_to admin_conversational_development_index_path, title: _('ConvDev Index') do
               %span
-                ConvDev Index
+                = _('ConvDev Index')
 
       = nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles)) do
         = link_to admin_system_info_path do
           .nav-icon-container
             = sprite_icon('monitor')
           %span.nav-item-name
-            Monitoring
+            = _('Monitoring')
 
         %ul.sidebar-sub-level-items
           = nav_link(controller: %w(system_info background_jobs logs health_check requests_profiles), html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_system_info_path do
               %strong.fly-out-top-item-name
-                #{ _('Monitoring') }
+                = _('Monitoring')
           %li.divider.fly-out-top-item
           = nav_link(controller: :system_info) do
-            = link_to admin_system_info_path, title: 'System Info' do
+            = link_to admin_system_info_path, title: _('System Info') do
               %span
-                System Info
+                = _('System Info')
           = nav_link(controller: :background_jobs) do
-            = link_to admin_background_jobs_path, title: 'Background Jobs' do
+            = link_to admin_background_jobs_path, title: _('Background Jobs') do
               %span
-                Background Jobs
+                = _('Background Jobs')
           = nav_link(controller: :logs) do
-            = link_to admin_logs_path, title: 'Logs' do
+            = link_to admin_logs_path, title: _('Logs') do
               %span
-                Logs
+                = _('Logs')
           = nav_link(controller: :health_check) do
-            = link_to admin_health_check_path, title: 'Health Check' do
+            = link_to admin_health_check_path, title: _('Health Check') do
               %span
-                Health Check
+                = _('Health Check')
           = nav_link(controller: :requests_profiles) do
-            = link_to admin_requests_profiles_path, title: 'Requests Profiles' do
+            = link_to admin_requests_profiles_path, title: _('Requests Profiles') do
               %span
-                Requests Profiles
+                = _('Requests Profiles')
 
       = nav_link(controller: :broadcast_messages) do
         = link_to admin_broadcast_messages_path do
           .nav-icon-container
             = sprite_icon('messages')
           %span.nav-item-name
-            Messages
+            = _('Messages')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :broadcast_messages, html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_broadcast_messages_path do
               %strong.fly-out-top-item-name
-                #{ _('Messages') }
+                = _('Messages')
 
       = nav_link(controller: [:hooks, :hook_logs]) do
         = link_to admin_hooks_path do
           .nav-icon-container
             = sprite_icon('hook')
           %span.nav-item-name
-            System Hooks
+            = _('System Hooks')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: [:hooks, :hook_logs], html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_hooks_path do
               %strong.fly-out-top-item-name
-                #{ _('System Hooks') }
+                = _('System Hooks')
 
       = nav_link(controller: :applications) do
         = link_to admin_applications_path do
           .nav-icon-container
             = sprite_icon('applications')
           %span.nav-item-name
-            Applications
+            = _('Applications')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :applications, html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_applications_path do
               %strong.fly-out-top-item-name
-                #{ _('Applications') }
+                = _('Applications')
 
       = nav_link(controller: :abuse_reports) do
         = link_to admin_abuse_reports_path do
           .nav-icon-container
             = sprite_icon('slight-frown')
           %span.nav-item-name
-            Abuse Reports
+            = _('Abuse Reports')
           %span.badge.badge-pill.count= number_with_delimiter(AbuseReport.count(:all))
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :abuse_reports, html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_abuse_reports_path do
               %strong.fly-out-top-item-name
-                #{ _('Abuse Reports') }
+                = _('Abuse Reports')
               %span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(AbuseReport.count(:all))
 
       - if akismet_enabled?
@@ -145,71 +146,71 @@
             .nav-icon-container
               = sprite_icon('spam')
             %span.nav-item-name
-              Spam Logs
+              = _('Spam Logs')
           %ul.sidebar-sub-level-items.is-fly-out-only
             = nav_link(controller: :spam_logs, html_options: { class: "fly-out-top-item" } ) do
               = link_to admin_spam_logs_path do
                 %strong.fly-out-top-item-name
-                  #{ _('Spam Logs') }
+                  = _('Spam Logs')
 
       = nav_link(controller: :deploy_keys) do
         = link_to admin_deploy_keys_path do
           .nav-icon-container
             = sprite_icon('key')
           %span.nav-item-name
-            Deploy Keys
+            = _('Deploy Keys')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :deploy_keys, html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_deploy_keys_path do
               %strong.fly-out-top-item-name
-                #{ _('Deploy Keys') }
+                = _('Deploy Keys')
 
       = nav_link(controller: :services) do
         = link_to admin_application_settings_services_path do
           .nav-icon-container
             = sprite_icon('template')
           %span.nav-item-name
-            Service Templates
+            = _('Service Templates')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :services, html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_application_settings_services_path do
               %strong.fly-out-top-item-name
-                #{ _('Service Templates') }
+                = _('Service Templates')
 
       = nav_link(controller: :labels) do
         = link_to admin_labels_path do
           .nav-icon-container
             = sprite_icon('labels')
           %span.nav-item-name
-            Labels
+            = _('Labels')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :labels, html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_labels_path do
               %strong.fly-out-top-item-name
-                #{ _('Labels') }
+                = _('Labels')
 
       = nav_link(controller: :appearances) do
         = link_to admin_appearances_path do
           .nav-icon-container
             = sprite_icon('appearance')
           %span.nav-item-name
-            Appearance
+            = _('Appearance')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :appearances, html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_appearances_path do
               %strong.fly-out-top-item-name
-                #{ _('Appearance') }
+                = _('Appearance')
 
       = nav_link(controller: :application_settings) do
         = link_to admin_application_settings_path do
           .nav-icon-container
             = sprite_icon('settings')
           %span.nav-item-name
-            Settings
+            = _('Settings')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :application_settings, html_options: { class: "fly-out-top-item" } ) do
             = link_to admin_application_settings_path do
               %strong.fly-out-top-item-name
-                #{ _('Settings') }
+                = _('Settings')
 
     = render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index 39a033337ff2efd78cc75903714ac7cbd7df3d02..0a3b5ec7eeaa74a93b6527cb4e89661c771a90dd 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -17,24 +17,24 @@
             .nav-icon-container
               = sprite_icon('project')
             %span.nav-item-name
-              Overview
+              = _('Overview')
 
           %ul.sidebar-sub-level-items
             = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do
               = link_to group_path(@group) do
                 %strong.fly-out-top-item-name
-                  #{ _('Overview') }
+                  = _('Overview')
             %li.divider.fly-out-top-item
             = nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
-              = link_to group_path(@group), title: 'Group details' do
+              = link_to group_path(@group), title: _('Group details') do
                 %span
-                  Details
+                  = _('Details')
 
             - if group_sidebar_link?(:activity)
               = nav_link(path: 'groups#activity') do
-                = link_to activity_group_path(@group), title: 'Activity' do
+                = link_to activity_group_path(@group), title: _('Activity') do
                   %span
-                    Activity
+                    = _('Activity')
 
       - if group_sidebar_link?(:issues)
         = nav_link(path: issues_sub_menu_items) do
@@ -42,21 +42,21 @@
             .nav-icon-container
               = sprite_icon('issues')
             %span.nav-item-name
-              Issues
+              = _('Issues')
             %span.badge.badge-pill.count= number_with_delimiter(issues_count)
 
           %ul.sidebar-sub-level-items
             = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do
               = link_to issues_group_path(@group) do
                 %strong.fly-out-top-item-name
-                  #{ _('Issues') }
+                  = _('Issues')
                 %span.badge.badge-pill.count.issue_counter.fly-out-badge= number_with_delimiter(issues_count)
 
             %li.divider.fly-out-top-item
             = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
-              = link_to issues_group_path(@group), title: 'List' do
+              = link_to issues_group_path(@group), title: _('List') do
                 %span
-                  List
+                  = _('List')
 
             - if group_sidebar_link?(:boards)
               = nav_link(path: ['boards#index', 'boards#show']) do
@@ -66,15 +66,15 @@
 
             - if group_sidebar_link?(:labels)
               = nav_link(path: 'labels#index') do
-                = link_to group_labels_path(@group), title: 'Labels' do
+                = link_to group_labels_path(@group), title: _('Labels') do
                   %span
-                    Labels
+                    = _('Labels')
 
             - if group_sidebar_link?(:milestones)
               = nav_link(path: 'milestones#index') do
-                = link_to group_milestones_path(@group), title: 'Milestones' do
+                = link_to group_milestones_path(@group), title: _('Milestones') do
                   %span
-                    Milestones
+                    = _('Milestones')
 
       - if group_sidebar_link?(:merge_requests)
         = nav_link(path: 'groups#merge_requests') do
@@ -82,13 +82,13 @@
             .nav-icon-container
               = sprite_icon('git-merge')
             %span.nav-item-name
-              Merge Requests
+              = _('Merge Requests')
             %span.badge.badge-pill.count= number_with_delimiter(merge_requests_count)
           %ul.sidebar-sub-level-items.is-fly-out-only
             = nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do
               = link_to merge_requests_group_path(@group) do
                 %strong.fly-out-top-item-name
-                  #{ _('Merge Requests') }
+                  = _('Merge Requests')
                 %span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests_count)
 
       - if group_sidebar_link?(:group_members)
@@ -97,12 +97,12 @@
             .nav-icon-container
               = sprite_icon('users')
             %span.nav-item-name
-              Members
+              = _('Members')
           %ul.sidebar-sub-level-items.is-fly-out-only
             = nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do
               = link_to group_group_members_path(@group) do
                 %strong.fly-out-top-item-name
-                  #{ _('Members') }
+                  = _('Members')
 
       - if group_sidebar_link?(:settings)
         = nav_link(path: group_nav_link_paths) do
@@ -110,17 +110,17 @@
             .nav-icon-container
               = sprite_icon('settings')
             %span.nav-item-name
-              Settings
+              = _('Settings')
           %ul.sidebar-sub-level-items
             = nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show], html_options: { class: "fly-out-top-item" } ) do
               = link_to edit_group_path(@group) do
                 %strong.fly-out-top-item-name
-                  #{ _('Settings') }
+                  = _('Settings')
             %li.divider.fly-out-top-item
             = nav_link(path: 'groups#edit') do
-              = link_to edit_group_path(@group), title: 'General' do
+              = link_to edit_group_path(@group), title: _('General') do
                 %span
-                  General
+                  = _('General')
 
             = nav_link(controller: :badges) do
               = link_to group_settings_badges_path(@group), title: _('Project Badges') do
@@ -129,13 +129,13 @@
 
 
             = nav_link(path: 'groups#projects') do
-              = link_to projects_group_path(@group), title: 'Projects' do
+              = link_to projects_group_path(@group), title: _('Projects') do
                 %span
-                  Projects
+                  = _('Projects')
 
             = nav_link(controller: :ci_cd) do
-              = link_to group_settings_ci_cd_path(@group), title: 'CI / CD' do
+              = link_to group_settings_ci_cd_path(@group), title: _('CI / CD') do
                 %span
-                  CI / CD
+                  = _('CI / CD')
 
     = render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index 6cbd163dd41b93b9af5dc49b37cd655accceca65..94863a3460dca35652b0ab8cde0182f9aa67a3bc 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -1,7 +1,7 @@
 .nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
   .nav-sidebar-inner-scroll
     .context-header
-      = link_to profile_path, title: 'Profile Settings' do
+      = link_to profile_path, title: _('Profile Settings') do
         .avatar-container.s40.settings-avatar
           = sprite_icon('user', size: 24)
         .sidebar-context-title User Settings
@@ -11,145 +11,145 @@
           .nav-icon-container
             = sprite_icon('profile')
           %span.nav-item-name
-            Profile
+            = _('Profile')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(path: 'profiles#show', html_options: { class: "fly-out-top-item" } ) do
             = link_to profile_path do
               %strong.fly-out-top-item-name
-                #{ _('Profile') }
+                = _('Profile')
       = nav_link(controller: [:accounts, :two_factor_auths]) do
         = link_to profile_account_path do
           .nav-icon-container
             = sprite_icon('account')
           %span.nav-item-name
-            Account
+            = _('Account')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: [:accounts, :two_factor_auths], html_options: { class: "fly-out-top-item" } ) do
             = link_to profile_account_path do
               %strong.fly-out-top-item-name
-                #{ _('Account') }
+                = _('Account')
       - if Gitlab::CurrentSettings.user_oauth_applications?
         = nav_link(controller: 'oauth/applications') do
           = link_to applications_profile_path do
             .nav-icon-container
               = sprite_icon('applications')
             %span.nav-item-name
-              Applications
+              = _('Applications')
           %ul.sidebar-sub-level-items.is-fly-out-only
             = nav_link(controller: 'oauth/applications', html_options: { class: "fly-out-top-item" } ) do
               = link_to applications_profile_path do
                 %strong.fly-out-top-item-name
-                  #{ _('Applications') }
+                  = _('Applications')
       = nav_link(controller: :chat_names) do
         = link_to profile_chat_names_path do
           .nav-icon-container
             = sprite_icon('comment')
           %span.nav-item-name
-            Chat
+            = _('Chat')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :chat_names, html_options: { class: "fly-out-top-item" } ) do
             = link_to profile_chat_names_path do
               %strong.fly-out-top-item-name
-                #{ _('Chat') }
+                = _('Chat')
       = nav_link(controller: :personal_access_tokens) do
         = link_to profile_personal_access_tokens_path do
           .nav-icon-container
             = sprite_icon('token')
           %span.nav-item-name
-            Access Tokens
+            = _('Access Tokens')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :personal_access_tokens, html_options: { class: "fly-out-top-item" } ) do
             = link_to profile_personal_access_tokens_path do
               %strong.fly-out-top-item-name
-                #{ _('Access Tokens') }
+                = _('Access Tokens')
       = nav_link(controller: :emails) do
         = link_to profile_emails_path do
           .nav-icon-container
             = sprite_icon('mail')
           %span.nav-item-name
-            Emails
+            = _('Emails')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :emails, html_options: { class: "fly-out-top-item" } ) do
             = link_to profile_emails_path do
               %strong.fly-out-top-item-name
-                #{ _('Emails') }
+                = _('Emails')
       - if current_user.allow_password_authentication?
         = nav_link(controller: :passwords) do
           = link_to edit_profile_password_path do
             .nav-icon-container
               = sprite_icon('lock')
             %span.nav-item-name
-              Password
+              = _('Password')
           %ul.sidebar-sub-level-items.is-fly-out-only
             = nav_link(controller: :passwords, html_options: { class: "fly-out-top-item" } ) do
               = link_to edit_profile_password_path do
                 %strong.fly-out-top-item-name
-                  #{ _('Password') }
+                  = _('Password')
       = nav_link(controller: :notifications) do
         = link_to profile_notifications_path do
           .nav-icon-container
             = sprite_icon('notifications')
           %span.nav-item-name
-            Notifications
+            = _('Notifications')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :notifications, html_options: { class: "fly-out-top-item" } ) do
             = link_to profile_notifications_path do
               %strong.fly-out-top-item-name
-                #{ _('Notifications') }
+                = _('Notifications')
       = nav_link(controller: :keys) do
         = link_to profile_keys_path do
           .nav-icon-container
             = sprite_icon('key')
           %span.nav-item-name
-            SSH Keys
+            = _('SSH Keys')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :keys, html_options: { class: "fly-out-top-item" } ) do
             = link_to profile_keys_path do
               %strong.fly-out-top-item-name
-                #{ _('SSH Keys') }
+                = _('SSH Keys')
       = nav_link(controller: :gpg_keys) do
         = link_to profile_gpg_keys_path do
           .nav-icon-container
             = sprite_icon('key-2')
           %span.nav-item-name
-            GPG Keys
+            = _('GPG Keys')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :gpg_keys, html_options: { class: "fly-out-top-item" } ) do
             = link_to profile_gpg_keys_path do
               %strong.fly-out-top-item-name
-                #{ _('GPG Keys') }
+                = _('GPG Keys')
       = nav_link(controller: :preferences) do
         = link_to profile_preferences_path do
           .nav-icon-container
             = sprite_icon('preferences')
           %span.nav-item-name
-            Preferences
+            = _('Preferences')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :preferences, html_options: { class: "fly-out-top-item" } ) do
             = link_to profile_preferences_path do
               %strong.fly-out-top-item-name
-                #{ _('Preferences') }
+                = _('Preferences')
       = nav_link(controller: :active_sessions) do
         = link_to profile_active_sessions_path do
           .nav-icon-container
             = sprite_icon('monitor-lines')
           %span.nav-item-name
-            Active Sessions
+            = _('Active Sessions')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(controller: :active_sessions, html_options: { class: "fly-out-top-item" } ) do
             = link_to profile_active_sessions_path do
               %strong.fly-out-top-item-name
-                #{ _('Active Sessions') }
+                = _('Active Sessions')
       = nav_link(path: 'profiles#audit_log') do
         = link_to audit_log_profile_path do
           .nav-icon-container
             = sprite_icon('log')
           %span.nav-item-name
-            Authentication log
+            = _('Authentication log')
         %ul.sidebar-sub-level-items.is-fly-out-only
           = nav_link(path: 'profiles#audit_log', html_options: { class: "fly-out-top-item" } ) do
             = link_to audit_log_profile_path do
               %strong.fly-out-top-item-name
-                #{ _('Authentication Log') }
+                = _('Authentication Log')
 
     = render 'shared/sidebar_toggle_button'
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 33de74dbaa24486b679885269e945f21573c78f6..0ec61df1f0a12510f28bf681df3e0bf1546e6436 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -105,7 +105,7 @@
                     = number_with_delimiter(@project.open_issues_count(current_user))
             %li.divider.fly-out-top-item
             = nav_link(controller: :issues, action: :index) do
-              = link_to project_issues_path(@project), title: 'Issues' do
+              = link_to project_issues_path(@project), title: _('Issues') do
                 %span
                   = _('List')
 
@@ -115,14 +115,14 @@
                   = boards_link_text
 
             = nav_link(controller: :labels) do
-              = link_to project_labels_path(@project), title: 'Labels' do
+              = link_to project_labels_path(@project), title: _('Labels') do
                 %span
                   = _('Labels')
 
             = render_if_exists 'projects/sidebar/issues_service_desk'
 
             = nav_link(controller: :milestones) do
-              = link_to project_milestones_path(@project), title: 'Milestones', class: 'qa-milestones-link' do
+              = link_to project_milestones_path(@project), title: _('Milestones'), class: 'qa-milestones-link' do
                 %span
                   = _('Milestones')
       - if project_nav_tab? :external_issue_tracker
@@ -172,25 +172,25 @@
             %li.divider.fly-out-top-item
             - if project_nav_tab? :pipelines
               = nav_link(path: ['pipelines#index', 'pipelines#show']) do
-                = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
+                = link_to project_pipelines_path(@project), title: _('Pipelines'), class: 'shortcuts-pipelines' do
                   %span
                     = _('Pipelines')
 
             - if project_nav_tab? :builds
               = nav_link(controller: [:jobs, :artifacts]) do
-                = link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
+                = link_to project_jobs_path(@project), title: _('Jobs'), class: 'shortcuts-builds' do
                   %span
                     = _('Jobs')
 
             - if project_nav_tab? :pipelines
               = nav_link(controller: :pipeline_schedules) do
-                = link_to pipeline_schedules_path(@project), title: 'Schedules', class: 'shortcuts-builds' do
+                = link_to pipeline_schedules_path(@project), title: _('Schedules'), class: 'shortcuts-builds' do
                   %span
                     = _('Schedules')
 
             - if @project.feature_available?(:builds, current_user) && !@project.empty_repo?
               = nav_link(path: 'pipelines#charts') do
-                = link_to charts_project_pipelines_path(@project), title: 'Charts', class: 'shortcuts-pipelines-charts' do
+                = link_to charts_project_pipelines_path(@project), title: _('Charts'), class: 'shortcuts-pipelines-charts' do
                   %span
                     = _('Charts')
 
@@ -242,7 +242,7 @@
                       %p= _('Allows you to add and manage Kubernetes clusters.')
                       %p
                         = _('Protip:')
-                        = link_to 'Auto DevOps', help_page_path('topics/autodevops/index.md')
+                        = link_to _('Auto DevOps'), help_page_path('topics/autodevops/index.md')
                         %span= _('uses Kubernetes clusters to deploy your code!')
                       %hr
                       %button.btn.btn-create.btn-sm.dismiss-feature-highlight{ type: 'button' }
@@ -305,11 +305,11 @@
                     = _('Settings')
               %li.divider.fly-out-top-item
               = nav_link(path: %w[projects#edit]) do
-                = link_to edit_project_path(@project), title: 'General' do
+                = link_to edit_project_path(@project), title: _('General') do
                   %span
                     = _('General')
             = nav_link(controller: :project_members) do
-              = link_to project_project_members_path(@project), title: 'Members' do
+              = link_to project_project_members_path(@project), title: _('Members') do
                 %span
                   = _('Members')
             - if can_edit
@@ -319,21 +319,21 @@
                     = _('Badges')
             - if can_edit
               = nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do
-                = link_to project_settings_integrations_path(@project), title: 'Integrations' do
+                = link_to project_settings_integrations_path(@project), title: _('Integrations') do
                   %span
                     = _('Integrations')
               = nav_link(controller: :repository) do
-                = link_to project_settings_repository_path(@project), title: 'Repository' do
+                = link_to project_settings_repository_path(@project), title: _('Repository') do
                   %span
                     = _('Repository')
               - if @project.feature_available?(:builds, current_user)
                 = nav_link(controller: :ci_cd) do
-                  = link_to project_settings_ci_cd_path(@project), title: 'CI / CD' do
+                  = link_to project_settings_ci_cd_path(@project), title: _('CI / CD') do
                     %span
                       = _('CI / CD')
               - if @project.pages_available?
                 = nav_link(controller: :pages) do
-                  = link_to project_pages_path(@project), title: 'Pages' do
+                  = link_to project_pages_path(@project), title: _('Pages') do
                     %span
                       = _('Pages')
 
@@ -341,7 +341,7 @@
 
       - else
         = nav_link(controller: :project_members) do
-          = link_to project_settings_members_path(@project), title: 'Members', class: 'shortcuts-tree' do
+          = link_to project_settings_members_path(@project), title: _('Members'), class: 'shortcuts-tree' do
             .nav-icon-container
               = sprite_icon('users')
             %span.nav-item-name
@@ -356,41 +356,41 @@
 
       -# Shortcut to Project > Activity
       %li.hidden
-        = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
+        = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do
           %span
-            Activity
+            = _('Activity')
 
       -# Shortcut to Repository > Graph (formerly, Network)
       - if project_nav_tab? :network
         %li.hidden
-          = link_to project_network_path(@project, current_ref), title: 'Network', class: 'shortcuts-network' do
-            Graph
+          = link_to project_network_path(@project, current_ref), title: _('Network'), class: 'shortcuts-network' do
+            = _('Graph')
 
       -# Shortcut to Repository > Charts (formerly, top-nav item "Graphs")
       - unless @project.empty_repo?
         %li.hidden
-          = link_to charts_project_graph_path(@project, current_ref), title: 'Charts', class: 'shortcuts-repository-charts' do
-            Charts
+          = link_to charts_project_graph_path(@project, current_ref), title: _('Charts'), class: 'shortcuts-repository-charts' do
+            = _('Charts')
 
       -# Shortcut to Issues > New Issue
       - if project_nav_tab?(:issues)
         %li.hidden
           = link_to new_project_issue_path(@project), class: 'shortcuts-new-issue' do
-            Create a new issue
+            = _('Create a new issue')
 
       -# Shortcut to Pipelines > Jobs
       - if project_nav_tab? :builds
         %li.hidden
-          = link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
-            Jobs
+          = link_to project_jobs_path(@project), title: _('Jobs'), class: 'shortcuts-builds' do
+            = _('Jobs')
 
       -# Shortcut to commits page
       - if project_nav_tab? :commits
         %li.hidden
-          = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
-            Commits
+          = link_to project_commits_path(@project), title: _('Commits'), class: 'shortcuts-commits' do
+            = _('Commits')
 
       -# Shortcut to issue boards
       - if project_nav_tab?(:issues)
         %li.hidden
-          = link_to 'Issue Boards', project_boards_path(@project), title: 'Issue Boards', class: 'shortcuts-issue-boards'
+          = link_to _('Issue Boards'), project_boards_path(@project), title: _('Issue Boards'), class: 'shortcuts-issue-boards'
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index ab8b1271212fb7bf93a48aad898d8a0bb18d0098..31e02f1ee1955475c875137884e48fd90fab791b 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -14,13 +14,12 @@
         %br
         - if @target_url
           - if @reply_by_email
-            Reply to this email directly or
-            #{link_to "view it on GitLab", @target_url}.
+            = _('Reply to this email directly or %{view_it_on_gitlab}.') % { view_it_on_gitlab: link_to(_("view it on GitLab"), @target_url) }
           - else
-            #{link_to "View it on GitLab", @target_url}.
+            #{link_to _("View it on GitLab"), @target_url}.
           %br
           -# Don't link the host in the line below, one link in the email is easier to quickly click than two.
-          You're receiving this email because #{notification_reason_text(@reason)}.
+          = _("You're receiving this email because %{reason}.") % { reason: notification_reason_text(@reason) }
           If you'd like to receive fewer emails, you can
           - if @labels_url
             adjust your #{link_to 'label subscriptions', @labels_url}.
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 67aa05b655c003694e9fe35394588a37b7c9aa47..7aca64663e0c1522f953aaee8e8fdfaf52518968 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -1,5 +1,5 @@
-- page_title    "User Settings"
-- header_title  "User Settings", profile_path unless header_title
+- page_title    _("User Settings")
+- header_title  _("User Settings"), profile_path unless header_title
 - sidebar       "dashboard"
 - nav           "profile"
 - @left_sidebar = true
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index 4bc94bd132daa3cc911c961a5c000163e3f28559..93214c2a674bb923da33d38a9fa726197818508c 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -1,4 +1,4 @@
-- page_title  "Settings"
+- page_title  _("Settings")
 - nav         "project"
 
 = render template: "layouts/project"
diff --git a/app/views/layouts/search.html.haml b/app/views/layouts/search.html.haml
index fd4c7ad21a71501bc03cf01dffb42e56ce38305d..dd4b9e452078aa523644341573a7b4eeac9faadc 100644
--- a/app/views/layouts/search.html.haml
+++ b/app/views/layouts/search.html.haml
@@ -1,4 +1,4 @@
-- page_title    "Search"
-- header_title  "Search", search_path
+- page_title    _("Search")
+- header_title  _("Search"), search_path
 
 = render template: "layouts/application"
diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml
index 849075a0ba5113d8691725bbc80418d85e0e8ff8..6418500d5d1666fa84b2263bd11f3c72f5052889 100644
--- a/app/views/layouts/snippets.html.haml
+++ b/app/views/layouts/snippets.html.haml
@@ -1,4 +1,4 @@
-- header_title  "Snippets", snippets_path
+- header_title  _("Snippets"), snippets_path
 
 - content_for :page_specific_javascripts do
   - if @snippet && current_user
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 507cd5dcc121482f33f4295f4ef9d2cc6a4045d5..a4835584b5010acf2e72b96130cc02bdd766694a 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -69,6 +69,12 @@
         = f.text_field :location
       = f.text_field :organization
       = f.text_area :bio, rows: 4, maxlength: 250, help: 'Tell us about yourself in fewer than 250 characters.'
+      %hr
+      %h5 Private profile
+      - private_profile_label = capture do
+        Don't display activity-related personal information on your profile
+        = link_to icon('question-circle'), help_page_path('user/profile/index.md', anchor: 'private-profile')
+      = f.check_box :private_profile, label: private_profile_label
       .prepend-top-default.append-bottom-default
         = f.submit 'Update profile settings', class: 'btn btn-success'
         = link_to 'Cancel', user_path(current_user), class: 'btn btn-cancel'
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index e90916e340d78a22107183cf2a3ea7038309670f..ef6f5c76de6d4f9dfb76f0c1bf06245f33e902b8 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -18,7 +18,7 @@
               - commit = blame_group[:commit]
               %td.blame-commit{ class: age_map_class(commit.committed_date, project_duration) }
                 .commit
-                  = author_avatar(commit, size: 36)
+                  = author_avatar(commit, size: 36, has_tooltip: false)
                   .commit-row-title
                     %span.item-title.str-truncated-100
                       = link_to_markdown commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index efb8175398b4f123bc40bd96f85f27ea78ca7644..5edab38bd6403db5aa09395a817caaac7475e8a4 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -3,6 +3,8 @@
 
 - page_title @blob.path, @ref
 
+.js-signature-container{ data: { 'signatures-path': namespace_project_signatures_path } }
+
 %div{ class: container_class }
   = render 'projects/last_push'
 
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index d8b4266143e3236653e7eb6432b3d72607548958..a2dc2730ecc6e5d60898e0235f074def9bf3249e 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,5 +1,5 @@
 - if current_user
-  %button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
+  %button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }>
     - if current_user.starred?(@project)
       = sprite_icon('star')
       %span.starred= _('Unstar')
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 886dd73c33b8d18654d088bebfdf46e71165615d..78522393d4b8049b44301e3ad5fb8bf6a8bded6b 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -10,7 +10,7 @@
     %span.d-none.d-sm-inline authored
     #{time_ago_with_tooltip(@commit.authored_date)}
     %span= s_('ByAuthor|by')
-    = author_avatar(@commit, size: 24)
+    = author_avatar(@commit, size: 24, has_tooltip: false)
     %strong
       = commit_author_link(@commit, avatar: true, size: 24)
     - if @commit.different_committer?
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 90e55fd0fb017313c61b713c418c43b6fa41ca68..feaf44e8c0a5f0cfec4d7b8ba5070e0f37f9ee4b 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -19,7 +19,7 @@
   %li.commit.flex-row.js-toggle-container{ id: "commit-#{commit.short_id}" }
 
     .avatar-cell.d-none.d-sm-block
-      = author_avatar(commit, size: 36)
+      = author_avatar(commit, size: 36, has_tooltip: false)
 
     .commit-detail.flex-list
       .commit-content.qa-commit-content
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index 04131a90a571a19569a7a15d6f41eb1daa083fbd..bc247460d28de7a61220c97467af9c8dad65d228 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -33,3 +33,5 @@
       %span.js-details-content.hide
         = link_to @pipeline.sha, project_commit_path(@project, @pipeline.sha), class: "commit-sha commit-hash-full"
       = clipboard_button(text: @pipeline.sha, title: "Copy commit SHA to clipboard")
+
+    = render_if_exists "projects/pipelines/info_extension", pipeline: @pipeline
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index 951f80b378d3aec0442011b5d36794a82fbf5c5d..c63ff070f709dac0cd0e0e3d8ebb1f72e4dad76b 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -12,6 +12,7 @@
         = link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do
           = _("Failed Jobs")
           %span.badge.badge-pill.js-failures-counter= @pipeline.failed_builds.count
+    = render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project
 
 .tab-content
   #js-tab-pipeline.tab-pane
@@ -75,4 +76,4 @@
                 %pre.build-trace.build-trace-rounded
                   %code.bash.js-build-output
                     = build_summary(build)
-
+  = render_if_exists "projects/pipelines/tabs_content", pipeline: @pipeline, project: @project
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index 956f8fef6b86b228af33cbe540b96faa1f00fd82..c13e3194340711882d9d3ca64e63b0296d07d00c 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -10,7 +10,7 @@
   = form_errors(@pipeline)
   .form-group.row
     .col-sm-12
-      = f.label :ref, s_('Pipeline|Create for')
+      = f.label :ref, s_('Pipeline|Create for'), class: 'col-form-label'
       = hidden_field_tag 'pipeline[ref]', params[:ref] || @project.default_branch
       = dropdown_tag(params[:ref] || @project.default_branch,
                      options: { toggle_class: 'js-branch-select wide git-revision-dropdown-toggle',
diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml
index 9741b783db33b523ab51c97b829ef139e5180a8c..1d0b0265bb7319ba2670426092881ab195a36205 100644
--- a/app/views/projects/services/prometheus/_show.html.haml
+++ b/app/views/projects/services/prometheus/_show.html.haml
@@ -7,4 +7,4 @@
       = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/metrics'), target: '_blank', rel: "noopener noreferrer"
 
   .col-lg-9
-    = render_if_exists 'projects/services/prometheus/metrics', project: @project
+    = render 'projects/services/prometheus/metrics', project: @project
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index e28accd5b434c9caa7fb617718293a79d8043546..803ecca48f703bef2c644422c50b4cf28f0d3bc7 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -8,6 +8,10 @@
 
 = render partial: 'flash_messages', locals: { project: @project }
 
+- if @project.repository_exists? && !@project.empty_repo?
+  - signatures_path = namespace_project_signatures_path(project_id: @project.path, id: @project.default_branch)
+  .js-signature-container{ data: { 'signatures-path': signatures_path } }
+
 %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
   = render "projects/last_push"
 
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index 3b4057e56d01b77a1a6a87b0cc1dde5409532234..ace8120eefffa0f6ec25786a725dbaacc0687e09 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -1,11 +1,14 @@
 - @no_container = true
 - breadcrumb_title _("Repository")
 - @content_class = "limit-container-width" unless fluid_layout
+- signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.path, project_id: @project.path, id: @ref)
 
 - page_title @path.presence || _("Files"), @ref
 = content_for :meta_tags do
   = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
 
+.js-signature-container{ data: { 'signatures-path': signatures_path } }
+
 %div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] }
   = render 'projects/last_push'
   = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id)
diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml
index e0832fd9136603ebebbd9a2fd002ce0d737b0dd3..9dd1c24fdfa6c3033da36eb153283397af9b105b 100644
--- a/app/views/shared/notes/_notes_with_form.html.haml
+++ b/app/views/shared/notes/_notes_with_form.html.haml
@@ -13,7 +13,7 @@
         .flash-container.timeline-content
 
         .timeline-icon.d-none.d-sm-none.d-md-block
-          %a.author_link{ href: user_path(current_user) }
+          %a.author-link{ href: user_path(current_user) }
             = image_tag avatar_icon_for_user(current_user), alt: current_user.to_reference, class: 'avatar s40'
         .timeline-content.timeline-content-form
           = render "shared/notes/form", view: diff_view, supports_autocomplete: autocomplete
diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml
index 37747faed62dfee23dc2ba1f805e2c6851e1900e..ddc089b0bd7c9100104c0ad043c3036476a69274 100644
--- a/app/views/sherlock/queries/_general.html.haml
+++ b/app/views/sherlock/queries/_general.html.haml
@@ -27,7 +27,7 @@
     .card-header
       .float-right
         %button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button }
-          %i.fa.fa-clipboard
+          = sprite_icon('duplicate')
         %pre.hidden
           = @query.formatted_query
       %strong
@@ -42,7 +42,7 @@
     .card-header
       .float-right
         %button.js-clipboard-trigger.btn.btn-sm{ title: t('sherlock.copy_to_clipboard'), type: :button }
-          %i.fa.fa-clipboard
+          = sprite_icon('duplicate')
         %pre.hidden
           = @query.explain
       %strong
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index b2ec7166832c8cdc369897e1a650656fd42b9187..8d9e86d02c40974899915aff048fbedbc0185d2d 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -23,8 +23,9 @@
           = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn',
             title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
             = icon('exclamation-circle')
-      = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do
-        = icon('rss')
+      - if can?(current_user, :read_user_profile, @user)
+        = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do
+          = icon('rss')
       - if current_user && current_user.admin?
         = link_to [:admin, @user], class: 'btn btn-default', title: 'View user in admin area',
           data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
@@ -40,10 +41,12 @@
           = @user.name
 
         .cover-desc.member-date
-          %span.middle-dot-divider
-            @#{@user.username}
-          %span.middle-dot-divider
-            Member since #{@user.created_at.to_date.to_s(:long)}
+          %p
+            %span.middle-dot-divider
+              @#{@user.username}
+            - if can?(current_user, :read_user_profile, @user)
+              %span.middle-dot-divider
+                Member since #{@user.created_at.to_date.to_s(:long)}
 
         .cover-desc
           - unless @user.public_email.blank?
@@ -78,30 +81,31 @@
         %p.profile-user-bio
           = @user.bio
 
-    .scrolling-tabs-container
-      .fade-left= icon('angle-left')
-      .fade-right= icon('angle-right')
-      %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs
-        - if profile_tab?(:activity)
-          %li.js-activity-tab
-            = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
-              Activity
-        - if profile_tab?(:groups)
-          %li.js-groups-tab
-            = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
-              Groups
-        - if profile_tab?(:contributed)
-          %li.js-contributed-tab
-            = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
-              Contributed projects
-        - if profile_tab?(:projects)
-          %li.js-projects-tab
-            = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
-              Personal projects
-        - if profile_tab?(:snippets)
-          %li.js-snippets-tab
-            = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
-              Snippets
+    - unless profile_tabs.empty?
+      .scrolling-tabs-container
+        .fade-left= icon('angle-left')
+        .fade-right= icon('angle-right')
+        %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs
+          - if profile_tab?(:activity)
+            %li.js-activity-tab
+              = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
+                Activity
+          - if profile_tab?(:groups)
+            %li.js-groups-tab
+              = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
+                Groups
+          - if profile_tab?(:contributed)
+            %li.js-contributed-tab
+              = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
+                Contributed projects
+          - if profile_tab?(:projects)
+            %li.js-projects-tab
+              = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
+                Personal projects
+          - if profile_tab?(:snippets)
+            %li.js-snippets-tab
+              = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
+                Snippets
 
   %div{ class: container_class }
     .tab-content
@@ -137,3 +141,13 @@
 
     .loading-status
       = spinner
+
+  - if profile_tabs.empty?
+    .row
+      .col-12
+        .svg-content
+          = image_tag 'illustrations/profile_private_mode.svg'
+      .col-12.text-center
+        .text-content
+          %h4
+            This user has a private profile
diff --git a/app/workers/concerns/each_shard_worker.rb b/app/workers/concerns/each_shard_worker.rb
index d0a728fb495f84c988f8498eaada715b73633669..00f589f957eddd988b8ff1b4d2c97e8559557e38 100644
--- a/app/workers/concerns/each_shard_worker.rb
+++ b/app/workers/concerns/each_shard_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module EachShardWorker
   extend ActiveSupport::Concern
   include ::Gitlab::Utils::StrongMemoize
diff --git a/app/workers/delete_diff_files_worker.rb b/app/workers/delete_diff_files_worker.rb
index bb8fbb9c3737238b034fee2235435f1a76ca5527..0874a0b75e884ded97bded5f081e7f3638e252a4 100644
--- a/app/workers/delete_diff_files_worker.rb
+++ b/app/workers/delete_diff_files_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class DeleteDiffFilesWorker
   include ApplicationWorker
 
diff --git a/app/workers/repository_check/dispatch_worker.rb b/app/workers/repository_check/dispatch_worker.rb
index 96634f09a15a15694286d2c0efdaa3aa9a2b34da..0a7d9a14c6a5433d35ceaf3f93f0070224f95708 100644
--- a/app/workers/repository_check/dispatch_worker.rb
+++ b/app/workers/repository_check/dispatch_worker.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module RepositoryCheck
   class DispatchWorker
     include ApplicationWorker
diff --git a/changelogs/unreleased/29278-commits-page-tooltips.yml b/changelogs/unreleased/29278-commits-page-tooltips.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d54301a1cf0c5706b20c5c8b413cfe979e9d881f
--- /dev/null
+++ b/changelogs/unreleased/29278-commits-page-tooltips.yml
@@ -0,0 +1,5 @@
+---
+title: Remove tooltips from commit author avatar and name in commit lists
+merge_request: 20674
+author:
+type: other
diff --git a/changelogs/unreleased/38604-add-private-profile.yml b/changelogs/unreleased/38604-add-private-profile.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e40e7d9321e8cbd63e78e648eb8dae4ea6165691
--- /dev/null
+++ b/changelogs/unreleased/38604-add-private-profile.yml
@@ -0,0 +1,5 @@
+---
+title: Add an option to have a private profile on GitLab.
+merge_request: 20387
+author: jxterry
+type: added
diff --git a/changelogs/unreleased/40973-disable-rack-attack-by-default.yml b/changelogs/unreleased/40973-disable-rack-attack-by-default.yml
new file mode 100644
index 0000000000000000000000000000000000000000..681aa761e2a5d44492cbdbaa1cec0f71f1303596
--- /dev/null
+++ b/changelogs/unreleased/40973-disable-rack-attack-by-default.yml
@@ -0,0 +1,5 @@
+---
+title: Rack attack is now disabled by default
+merge_request: 16669
+author:
+type: changed
diff --git a/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml b/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b60aeba860a154a11a8fdb2fc32fdb0121e0abc7
--- /dev/null
+++ b/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Vue datatype errors for markdownVersion parsing
+merge_request: 20800
+author:
+type: fixed
diff --git a/changelogs/unreleased/45318-vuex-store.yml b/changelogs/unreleased/45318-vuex-store.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5ea89034bce06a0185ac6a58c4a75fa5a17dfed5
--- /dev/null
+++ b/changelogs/unreleased/45318-vuex-store.yml
@@ -0,0 +1,5 @@
+---
+title: Adds Vuex store for reports section in MR widget
+merge_request: 20709
+author:
+type: added
diff --git a/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml b/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d490df581445489460242eaa29b9eb81b6be2443
--- /dev/null
+++ b/changelogs/unreleased/46869-deploy-tokens-failed-to-clone-lfs-repository.yml
@@ -0,0 +1,5 @@
+---
+title: Allow cloning LFS repositories through DeployTokens
+merge_request: 20729
+author:
+type: other
diff --git a/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml b/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml
deleted file mode 100644
index 7552e0d38781b3ea3d79ec3d76fe36cde4971a6b..0000000000000000000000000000000000000000
--- a/changelogs/unreleased/48745-project-exports-fail-when-uploads-have-been-migrated-to-object-storage.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add uploader support to Import/Export uploads
-merge_request: 20484
-author:
-type: added
diff --git a/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml b/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0466debea65d07e4840f5a0acf4995c30eed5eb7
--- /dev/null
+++ b/changelogs/unreleased/48932-disable-saml-if-omniauth-is-disabled.yml
@@ -0,0 +1,5 @@
+---
+title: Disable SAML and Bitbucket if OmniAuth is disabled
+merge_request: 20608
+author:
+type: fixed
diff --git a/changelogs/unreleased/49025-docs-kubernetes-tiller.yml b/changelogs/unreleased/49025-docs-kubernetes-tiller.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c4f01490cfa21cc2c5e058668bed7ca8057347b3
--- /dev/null
+++ b/changelogs/unreleased/49025-docs-kubernetes-tiller.yml
@@ -0,0 +1,5 @@
+---
+title: Update docs of Helm Tiller
+merge_request: 20515
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml b/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c757e55f1cd43a9a9e29392c1ff54f2ef49f523b
--- /dev/null
+++ b/changelogs/unreleased/49272-sanitize-git-url-in-import-errors.yml
@@ -0,0 +1,5 @@
+---
+title: Sanitize git URL in import errors
+merge_request:
+author: Jamie Schembri
+type: fixed
diff --git a/changelogs/unreleased/api-minimal-access-level.yml b/changelogs/unreleased/api-minimal-access-level.yml
new file mode 100644
index 0000000000000000000000000000000000000000..43cab246d6974ced9ff06fcfe5085f9ca793489a
--- /dev/null
+++ b/changelogs/unreleased/api-minimal-access-level.yml
@@ -0,0 +1,5 @@
+---
+title: Add filter for minimal access level in groups and projects API
+merge_request: 20478
+author: Marko, Peter
+type: added
diff --git a/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml
new file mode 100644
index 0000000000000000000000000000000000000000..69e6b7d815a292c83df5f23fd8a89334ff1916e1
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-replace-sidekiq-inline-with-perform-enqueued-jobs.yml
@@ -0,0 +1,5 @@
+---
+title: Replace 'Sidekiq::Testing.inline!' with 'perform_enqueued_jobs'
+merge_request: 20768
+author: "@blackst0ne"
+type: other
diff --git a/changelogs/unreleased/fix-filename-for-direct-uploads.yml b/changelogs/unreleased/fix-filename-for-direct-uploads.yml
deleted file mode 100644
index a1bbf3704c0c7f26f6633ba5f7a7dc56ab801d30..0000000000000000000000000000000000000000
--- a/changelogs/unreleased/fix-filename-for-direct-uploads.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix filename for accelerated uploads
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-48123-fix-gitlab-import.yml b/changelogs/unreleased/fj-48123-fix-gitlab-import.yml
new file mode 100644
index 0000000000000000000000000000000000000000..896db2cdcb884ad60f0ff6e586a9ebaffecf1341
--- /dev/null
+++ b/changelogs/unreleased/fj-48123-fix-gitlab-import.yml
@@ -0,0 +1,5 @@
+---
+title: Fix GitLab project imports not loading due to API timeouts
+merge_request: 20599
+author:
+type: fixed
diff --git a/changelogs/unreleased/frozen-string-danger.yml b/changelogs/unreleased/frozen-string-danger.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9910139b8a95072bd9057536901bad3309ed4d99
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-danger.yml
@@ -0,0 +1,5 @@
+---
+title: Add Dangerfile for frozen_string_literal
+merge_request: 20767
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml b/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5c6b1b1a90410bc126d3b8d6c5a9c3635f9250b0
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-presenters-policies.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in app/presenters and app/policies
+merge_request: 20819
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-enable-app-serializers.yml b/changelogs/unreleased/frozen-string-enable-app-serializers.yml
new file mode 100644
index 0000000000000000000000000000000000000000..40c7b695d3974b0bcddb240e6e0561d0b055e2a0
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-enable-app-serializers.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in app/serializers/**/*.rb
+merge_request: 20726
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/frozen-string-vestigial.yml b/changelogs/unreleased/frozen-string-vestigial.yml
new file mode 100644
index 0000000000000000000000000000000000000000..79e92a19a7a8ac11d60e33a0b1a98767995adb77
--- /dev/null
+++ b/changelogs/unreleased/frozen-string-vestigial.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen string in newly added files to previously processed directories
+merge_request: 20763
+author: gfyoung
+type: performance
diff --git a/changelogs/unreleased/gitaly-ff-branch-nil.yml b/changelogs/unreleased/gitaly-ff-branch-nil.yml
deleted file mode 100644
index e7e689e605394bd4d89ea4dd804464d5a9706fb6..0000000000000000000000000000000000000000
--- a/changelogs/unreleased/gitaly-ff-branch-nil.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add missing Gitaly branch_update nil checks
-merge_request: 20711
-author:
-type: fixed
diff --git a/changelogs/unreleased/hangouts_chat_integration.yml b/changelogs/unreleased/hangouts_chat_integration.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bf3484a6d0202e90a7f6cf212848ae1a028c3f75
--- /dev/null
+++ b/changelogs/unreleased/hangouts_chat_integration.yml
@@ -0,0 +1,5 @@
+---
+title: Add Hangouts Chat integration
+merge_request: 20290
+author: Kukovskii Vladimir
+type: added
diff --git a/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a2fca4c5b91cabc0cee456219fca0c3b6d5eb0da
--- /dev/null
+++ b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml
@@ -0,0 +1,5 @@
+---
+title: Show decimal place up to single digit in Stacked Progress Bar
+merge_request: 20776
+author:
+type: changed
diff --git a/changelogs/unreleased/project-visibility-tooltip.yml b/changelogs/unreleased/project-visibility-tooltip.yml
new file mode 100644
index 0000000000000000000000000000000000000000..806c93e493a6d7309e5ecaa150f2bb9e2b024bba
--- /dev/null
+++ b/changelogs/unreleased/project-visibility-tooltip.yml
@@ -0,0 +1,5 @@
+---
+title: Fix project visibility tooltip
+merge_request: 20535
+author: Jamie Schembri
+type: fixed
diff --git a/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml b/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8d5ecdfa57eb6c8d0d08577697a5c4a3080922c9
--- /dev/null
+++ b/changelogs/unreleased/replace-all-snake-case-in-scss-variables.yml
@@ -0,0 +1,5 @@
+---
+title: Replace snake case in SCSS variables
+merge_request: 20799
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/replace-snake-case-css-classes.yml b/changelogs/unreleased/replace-snake-case-css-classes.yml
new file mode 100644
index 0000000000000000000000000000000000000000..28ec5ee097fc2f82a0aad442cb4f3d05c211a833
--- /dev/null
+++ b/changelogs/unreleased/replace-snake-case-css-classes.yml
@@ -0,0 +1,5 @@
+---
+title: Replace author_link snake case in stylesheets, specs, and helpers
+merge_request: 20797
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b9444440cb9d5da9e0fd935719d74f96908b2e83
--- /dev/null
+++ b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml
@@ -0,0 +1,5 @@
+---
+title: Bump nokogiri to 1.8.4 and sanitize to 4.6.6 for performance
+merge_request: 20795
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml
new file mode 100644
index 0000000000000000000000000000000000000000..897d673e97d59086aeac71396cfcdc3e71e66f63
--- /dev/null
+++ b/changelogs/unreleased/sh-enable-frozen-literals-banzi-filters.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen strings in remaining lib/banzai/filter/*.rb files
+merge_request: 20777
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml
new file mode 100644
index 0000000000000000000000000000000000000000..37b397ea49f1291cfec599b65581c338c028ab9a
--- /dev/null
+++ b/changelogs/unreleased/sh-freeze-banzai-filter-strings.yml
@@ -0,0 +1,5 @@
+---
+title: Enable frozen strings in lib/banzai/filter/*.rb
+merge_request: 20775
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-remove-banzai-instrumentation.yml b/changelogs/unreleased/sh-remove-banzai-instrumentation.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8bb3cd5942b0b0c6e3265c508c620c8bea7f4daf
--- /dev/null
+++ b/changelogs/unreleased/sh-remove-banzai-instrumentation.yml
@@ -0,0 +1,5 @@
+---
+title: Remove method instrumentation for Banzai filters and reference parsers
+merge_request: 20770
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-revert-markdown-changes.yml b/changelogs/unreleased/sh-revert-markdown-changes.yml
new file mode 100644
index 0000000000000000000000000000000000000000..72540f710a1235176eb44986d0063dd9593e194b
--- /dev/null
+++ b/changelogs/unreleased/sh-revert-markdown-changes.yml
@@ -0,0 +1,5 @@
+---
+title: Fix slow Markdown rendering
+merge_request: 20820
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml b/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e8c2e11ad31c61a9840dc6b7028fd1d63c1c76ef
--- /dev/null
+++ b/changelogs/unreleased/sh-use-wiki-limit-parameter-gitaly.yml
@@ -0,0 +1,5 @@
+---
+title: Use limit parameter to retrieve Wikis from Gitaly
+merge_request: 20764
+author:
+type: performance
diff --git a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml
new file mode 100644
index 0000000000000000000000000000000000000000..16003fa9cadad539ce3d0a1b12139483a0d5b0f6
--- /dev/null
+++ b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml
@@ -0,0 +1,5 @@
+---
+title: Reduces the client side memory footprint on merge requests
+merge_request: 20744
+author:
+type: performance
diff --git a/changelogs/unreleased/winh-tree-view-gpg.yml b/changelogs/unreleased/winh-tree-view-gpg.yml
new file mode 100644
index 0000000000000000000000000000000000000000..84d63814a47c02f5173ecf2cf70675ac3d3c9008
--- /dev/null
+++ b/changelogs/unreleased/winh-tree-view-gpg.yml
@@ -0,0 +1,5 @@
+---
+title: Display GPG status on repository and blob pages
+merge_request: 20524
+author:
+type: changed
diff --git a/changelogs/unreleased/zj-backup-timeout.yml b/changelogs/unreleased/zj-backup-timeout.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b2ad2ed8c63e4efefff8fde9627e853605aa79bd
--- /dev/null
+++ b/changelogs/unreleased/zj-backup-timeout.yml
@@ -0,0 +1,5 @@
+---
+title: Disable Gitaly timeouts when creating or restoring backups
+merge_request: 20810
+author:
+type: fixed
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index ab109f5d04fcb0d6cbc23b5701ffac37c5ea2aaa..561ff57c9fb652243234e7cc146175b07d5759d0 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -78,15 +78,15 @@ production: &base
     # username_changing_enabled: false # default: true - User can change her username/namespace
     ## Default theme ID
     ##   1 - Indigo
-    ##   2 - Light Indigo
-    ##   3 - Blue
-    ##   4 - Light Blue
+    ##   2 - Dark
+    ##   3 - Light
+    ##   4 - Blue
     ##   5 - Green
-    ##   6 - Light Green
-    ##   7 - Red
-    ##   8 - Light Red
-    ##   9 - Dark
-    ##   10 - Light
+    ##   6 - Light Indigo
+    ##   7 - Light Blue
+    ##   8 - Light Green
+    ##   9 - Red
+    ##   10 - Light Red
     # default_theme: 1 # default: 1
 
     ## Automatic issue closing
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index c3122827a6bc5c93827863ea1c0806cc13a363ce..9ad55e21d11c9ff7c5521d9a1c93ac28e1d8a5ab 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -1,4 +1,5 @@
 require_relative '../settings'
+require_relative '../object_store_settings'
 
 # Default settings
 Settings['ldap'] ||= Settingslogic.new({})
@@ -179,14 +180,7 @@ Settings.artifacts['storage_path'] = Settings.absolute(Settings.artifacts.values
 # Settings.artifact['path'] is deprecated, use `storage_path` instead
 Settings.artifacts['path']         = Settings.artifacts['storage_path']
 Settings.artifacts['max_size'] ||= 100 # in megabytes
-Settings.artifacts['object_store'] ||= Settingslogic.new({})
-Settings.artifacts['object_store']['enabled'] = false if Settings.artifacts['object_store']['enabled'].nil?
-Settings.artifacts['object_store']['remote_directory'] ||= nil
-Settings.artifacts['object_store']['direct_upload'] = false if Settings.artifacts['object_store']['direct_upload'].nil?
-Settings.artifacts['object_store']['background_upload'] = true if Settings.artifacts['object_store']['background_upload'].nil?
-Settings.artifacts['object_store']['proxy_download'] = false if Settings.artifacts['object_store']['proxy_download'].nil?
-# Convert upload connection settings to use string keys, to make Fog happy
-Settings.artifacts['object_store']['connection']&.deep_stringify_keys!
+Settings.artifacts['object_store'] = ObjectStoreSettings.parse(Settings.artifacts['object_store'])
 
 #
 # Registry
@@ -225,14 +219,7 @@ Settings.pages.admin['certificate'] ||= ''
 Settings['lfs'] ||= Settingslogic.new({})
 Settings.lfs['enabled']      = true if Settings.lfs['enabled'].nil?
 Settings.lfs['storage_path'] = Settings.absolute(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects"))
-Settings.lfs['object_store'] ||= Settingslogic.new({})
-Settings.lfs['object_store']['enabled'] = false if Settings.lfs['object_store']['enabled'].nil?
-Settings.lfs['object_store']['remote_directory'] ||= nil
-Settings.lfs['object_store']['direct_upload'] = false if Settings.lfs['object_store']['direct_upload'].nil?
-Settings.lfs['object_store']['background_upload'] = true if Settings.lfs['object_store']['background_upload'].nil?
-Settings.lfs['object_store']['proxy_download'] = false if Settings.lfs['object_store']['proxy_download'].nil?
-# Convert upload connection settings to use string keys, to make Fog happy
-Settings.lfs['object_store']['connection']&.deep_stringify_keys!
+Settings.lfs['object_store'] = ObjectStoreSettings.parse(Settings.lfs['object_store'])
 
 #
 # Uploads
@@ -240,14 +227,8 @@ Settings.lfs['object_store']['connection']&.deep_stringify_keys!
 Settings['uploads'] ||= Settingslogic.new({})
 Settings.uploads['storage_path'] = Settings.absolute(Settings.uploads['storage_path'] || 'public')
 Settings.uploads['base_dir'] = Settings.uploads['base_dir'] || 'uploads/-/system'
-Settings.uploads['object_store'] ||= Settingslogic.new({})
-Settings.uploads['object_store']['enabled'] = false if Settings.uploads['object_store']['enabled'].nil?
+Settings.uploads['object_store'] = ObjectStoreSettings.parse(Settings.uploads['object_store'])
 Settings.uploads['object_store']['remote_directory'] ||= 'uploads'
-Settings.uploads['object_store']['direct_upload'] = false if Settings.uploads['object_store']['direct_upload'].nil?
-Settings.uploads['object_store']['background_upload'] = true if Settings.uploads['object_store']['background_upload'].nil?
-Settings.uploads['object_store']['proxy_download'] = false if Settings.uploads['object_store']['proxy_download'].nil?
-# Convert upload connection settings to use string keys, to make Fog happy
-Settings.uploads['object_store']['connection']&.deep_stringify_keys!
 
 #
 # Mattermost
@@ -437,7 +418,7 @@ Settings['extra'] ||= Settingslogic.new({})
 #
 Settings['rack_attack'] ||= Settingslogic.new({})
 Settings.rack_attack['git_basic_auth'] ||= Settingslogic.new({})
-Settings.rack_attack.git_basic_auth['enabled'] = true if Settings.rack_attack.git_basic_auth['enabled'].nil?
+Settings.rack_attack.git_basic_auth['enabled'] = false if Settings.rack_attack.git_basic_auth['enabled'].nil?
 Settings.rack_attack.git_basic_auth['ip_whitelist'] ||= %w{127.0.0.1}
 Settings.rack_attack.git_basic_auth['maxretry'] ||= 10
 Settings.rack_attack.git_basic_auth['findtime'] ||= 1.minute
diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb
index 8a851b89c56f1e783d3c7f70df57c86dde2dde7e..4d8d35bf6cf402135af605c23b3d15ca5f54bfcc 100644
--- a/config/initializers/8_metrics.rb
+++ b/config/initializers/8_metrics.rb
@@ -58,20 +58,6 @@ def instrument_classes(instrumentation)
     instrumentation.instrument_instance_methods(const)
   end
 
-  # Instruments all Banzai filters and reference parsers
-  {
-    Filter: Rails.root.join('lib', 'banzai', 'filter', '*.rb'),
-    ReferenceParser: Rails.root.join('lib', 'banzai', 'reference_parser', '*.rb')
-  }.each do |const_name, path|
-    Dir[path].each do |file|
-      klass = File.basename(file, File.extname(file)).camelize
-      const = Banzai.const_get(const_name).const_get(klass)
-
-      instrumentation.instrument_methods(const)
-      instrumentation.instrument_instance_methods(const)
-    end
-  end
-
   instrumentation.instrument_methods(Banzai::Renderer)
   instrumentation.instrument_methods(Banzai::Querying)
 
@@ -94,8 +80,6 @@ def instrument_classes(instrumentation)
 
   instrumentation.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker)
 
-  instrumentation.instrument_instance_methods(Rouge::Plugins::CommonMark)
-  instrumentation.instrument_instance_methods(Rouge::Plugins::Redcarpet)
   instrumentation.instrument_instance_methods(Rouge::Formatters::HTMLGitlab)
 
   [:XML, :HTML].each do |namespace|
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index e5772c3330797f4cb7478b573bdf46dfee871044..c41b2db722c6b18f368e6ce927cfa9365b015daf 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -219,7 +219,7 @@ Devise.setup do |config|
     end
   end
 
-  if Gitlab::OmniauthInitializer.enabled?
+  if Gitlab::Auth.omniauth_enabled?
     Gitlab::OmniauthInitializer.new(config).execute(Gitlab.config.omniauth.providers)
   end
 end
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index c558eb28ced45a95a7f6c7f56aac4fdf8ffcadf5..ef23ca065c612340f43f46d9e69461ba25cdf682 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -16,8 +16,3 @@ OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_s
 OmniAuth.config.before_request_phase do |env|
   Gitlab::RequestForgeryProtection.call(env)
 end
-
-if Gitlab::OmniauthInitializer.enabled?
-  provider_names = Gitlab.config.omniauth.providers.map(&:name)
-  Gitlab::Auth.omniauth_setup_providers(provider_names)
-end
diff --git a/config/object_store_settings.rb b/config/object_store_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d85ff394dcc180ffb35671bc1e4865a5bde4f43b
--- /dev/null
+++ b/config/object_store_settings.rb
@@ -0,0 +1,15 @@
+# Set default values for object_store settings
+class ObjectStoreSettings
+  def self.parse(object_store)
+    object_store ||= Settingslogic.new({})
+    object_store['enabled'] = false if object_store['enabled'].nil?
+    object_store['remote_directory'] ||= nil
+    object_store['direct_upload'] = false if object_store['direct_upload'].nil?
+    object_store['background_upload'] = true if object_store['background_upload'].nil?
+    object_store['proxy_download'] = false if object_store['proxy_download'].nil?
+
+    # Convert upload connection settings to use string keys, to make Fog happy
+    object_store['connection']&.deep_stringify_keys!
+    object_store
+  end
+end
diff --git a/danger/frozen_string/Dangerfile b/danger/frozen_string/Dangerfile
new file mode 100644
index 0000000000000000000000000000000000000000..595176d597d6e4819857f431f0ecf2a74a3513e5
--- /dev/null
+++ b/danger/frozen_string/Dangerfile
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+FILE_EXTENSION = ".rb"
+MAGIC_COMMENT = "# frozen_string_literal: true"
+
+def get_files_with_no_magic_comment(files)
+  files.select do |file|
+    file.end_with?(FILE_EXTENSION) &&
+      !File.open(file, &:gets).start_with?(MAGIC_COMMENT)
+  end
+end
+
+files_to_check = git.added_files
+files_to_fix = get_files_with_no_magic_comment(files_to_check)
+
+if files_to_fix.any?
+  warn 'This merge request adds files that do not enforce frozen string literal. ' \
+    'See https://gitlab.com/gitlab-org/gitlab-ce/issues/47424 for more information.'
+
+  markdown(<<~MARKDOWN)
+## Enable Frozen String Literal
+
+The following files should have `#{MAGIC_COMMENT}` in the first line:
+
+* #{files_to_fix.map { |path| "`#{path}`" }.join("\n* ")}
+  MARKDOWN
+end
diff --git a/db/migrate/20180722103201_add_private_profile_to_users.rb b/db/migrate/20180722103201_add_private_profile_to_users.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4f7ef1322d8cf7bdd69a3cad7eb6a833320d7dfa
--- /dev/null
+++ b/db/migrate/20180722103201_add_private_profile_to_users.rb
@@ -0,0 +1,10 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddPrivateProfileToUsers < ActiveRecord::Migration
+  DOWNTIME = false
+
+  def change
+    add_column :users, :private_profile, :boolean
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 1a5555fb3a4f5edc02d597be0984fd642d7265bf..3db11d8447ec9ee5b9ae3b3881fbf5d95af1b9b2 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20180704204006) do
+ActiveRecord::Schema.define(version: 20180722103201) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -2124,6 +2124,7 @@ ActiveRecord::Schema.define(version: 20180704204006) do
     t.integer "theme_id", limit: 2
     t.integer "accepted_term_id"
     t.string "feed_token"
+    t.boolean "private_profile"
   end
 
   add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md
index 0d9c10687f2993bde150fcb0ce0a5665516ab6f1..637d44d2823fdb3420582cc445359d0ff0c2fea3 100644
--- a/doc/administration/high_availability/gitlab.md
+++ b/doc/administration/high_availability/gitlab.md
@@ -122,6 +122,11 @@ need some extra configuration.
    from running on upgrade. Only the primary GitLab application server should
    handle migrations.
 
+1. **Optional** Configure host keys. Copy all contents(primary and public keys) inside `/etc/ssh/` on
+   the primary application server to `/etc/ssh` on all secondary servers. This
+   prevents false man-in-the-middle-attack alerts when accessing servers in your
+   High Availability cluster behind a load balancer.
+
 1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
 
 ## Troubleshooting
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index c0e98099e4262f993def6e7b3ba3ce1a55647533..7bc92ae77eefa3a6c67a1eb98ad449664aa0ccd4 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -22,7 +22,7 @@ collect metrics from this endpoint. We recommend setting up another Prometheus
 server, because the embedded server configuration is overwritten once every
 [reconfigure of GitLab][reconfigure]. In the future this will not be required.
 
-## Metrics available
+## Unicorn Metrics available
 
 The following metrics are available:
 
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index b3602bc35ab687986a25b1d1926f9616dad113f8..eefa86f8e42a74a7e5d10d0f2e9520a3f6b86c31 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -124,11 +124,6 @@ The Pages daemon doesn't listen to the outside world.
     ```
 
 1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
-     ```shell
-    sudo gitlab-ctl restart gitlab-pages
-    ```
 
 
 Watch the [video tutorial][video-admin] for this configuration.
@@ -161,11 +156,6 @@ outside world.
     respectively.
 
 1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
-     ```shell
-    sudo gitlab-ctl restart gitlab-pages
-    ```
 
 ## Advanced configuration
 
@@ -203,11 +193,6 @@ world. Custom domains are supported, but no TLS.
     listens on. If you don't have IPv6, you can omit the IPv6 address.
 
 1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
-     ```shell
-    sudo gitlab-ctl restart gitlab-pages
-    ```
 
 ### Custom domains with TLS support
 
@@ -241,11 +226,6 @@ world. Custom domains and TLS are supported.
     listens on. If you don't have IPv6, you can omit the IPv6 address.
 
 1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
-
-     ```shell
-    sudo gitlab-ctl restart gitlab-pages
-    ```
 
 ### Custom domain verification
 
@@ -267,7 +247,6 @@ Omnibus GitLab 11.1.
 Follow the steps below to configure verbose logging of GitLab Pages daemon.
 
 1. By default the daemon only logs with `INFO` level.
-
    If you wish to make it log events with level `DEBUG` you must configure this in
    `/etc/gitlab/gitlab.rb`:
 
@@ -291,11 +270,29 @@ are stored.
      ```
 
 1. [Reconfigure GitLab][reconfigure]
-1. Restart gitlab-pages by running the following command:
+
+## Configure listener for reverse proxy requests
+
+Follow the steps below to configure the proxy listener of GitLab Pages. [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2533) in
+Omnibus GitLab 11.1.
+
+1. By default the listener is configured to listen for requests on `localhost:8090`.
+
+   If you wish to disable it you must configure this in
+   `/etc/gitlab/gitlab.rb`:
 
      ```shell
-    sudo gitlab-ctl restart gitlab-pages
-    ```
+     gitlab_pages['listen_proxy'] = nil
+     ```
+
+   If you wish to make it listen on a different port you must configure this also in
+   `/etc/gitlab/gitlab.rb`:
+
+     ```shell
+     gitlab_pages['listen_proxy'] = "localhost:10080"
+     ```
+
+1. [Reconfigure GitLab][reconfigure]
 
 ## Set maximum pages size
 
diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md
index 2649bf61d74c94d30ad6b43f55f639ebe1edffd0..0c145830f027507db93d8939496a75e57b93a2b7 100644
--- a/doc/administration/raketasks/check.md
+++ b/doc/administration/raketasks/check.md
@@ -121,6 +121,45 @@ sudo gitlab-rake gitlab:lfs:check BATCH=100 ID_FROM=50 ID_TO=250
 sudo gitlab-rake gitlab:uploads:check BATCH=100 ID_FROM=50 ID_TO=250
 ```
 
+Example output:
+
+```
+$ sudo gitlab-rake gitlab:uploads:check
+Checking integrity of Uploads
+- 1..1350: Failures: 0
+- 1351..2743: Failures: 0
+- 2745..4349: Failures: 2
+- 4357..5762: Failures: 1
+- 5764..7140: Failures: 2
+- 7142..8651: Failures: 0
+- 8653..10134: Failures: 0
+- 10135..11773: Failures: 0
+- 11777..13315: Failures: 0
+Done!
+```
+
+Example verbose output:
+
+```
+$ sudo gitlab-rake gitlab:uploads:check VERBOSE=1
+Checking integrity of Uploads
+- 1..1350: Failures: 0
+- 1351..2743: Failures: 0
+- 2745..4349: Failures: 2
+  - Upload: 3573: #<Errno::ENOENT: No such file or directory @ rb_sysopen - /opt/gitlab/embedded/service/gitlab-rails/public/uploads/user-foo/project-bar/7a77cc52947bfe188adeff42f890bb77/image.png>
+  - Upload: 3580: #<Errno::ENOENT: No such file or directory @ rb_sysopen - /opt/gitlab/embedded/service/gitlab-rails/public/uploads/user-foo/project-bar/2840ba1ba3b2ecfa3478a7b161375f8a/pug.png>
+- 4357..5762: Failures: 1
+  - Upload: 4636: #<Google::Apis::ServerError: Server error>
+- 5764..7140: Failures: 2
+  - Upload: 5812: #<NoMethodError: undefined method `hashed_storage?' for nil:NilClass>
+  - Upload: 5837: #<NoMethodError: undefined method `hashed_storage?' for nil:NilClass>
+- 7142..8651: Failures: 0
+- 8653..10134: Failures: 0
+- 10135..11773: Failures: 0
+- 11777..13315: Failures: 0
+Done!
+```
+
 ## LDAP Check
 
 The LDAP check Rake task will test the bind_dn and password credentials
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 11de75039eedbcf12e1fcff3ff73c830cc1b72b1..87be36cc81512ccb929fa93f43e10c04bb73f9ca 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -10,13 +10,14 @@ Parameters:
 | Attribute | Type | Required | Description |
 | --------- | ---- | -------- | ----------- |
 | `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
 | `search` | string | no | Return the list of authorized groups matching the search criteria |
 | `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
 | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
 | `statistics` | boolean | no | Include group statistics (admins only) |
 | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `owned` | boolean | no | Limit to groups owned by the current user |
+| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
 
 ```
 GET /groups
@@ -94,13 +95,14 @@ Parameters:
 | --------- | ---- | -------- | ----------- |
 | `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group |
 | `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin); Attributes `owned` and `min_access_level` have precedence |
 | `search` | string | no | Return the list of authorized groups matching the search criteria |
 | `order_by` | string | no | Order groups by `name`, `path` or `id`. Default is `name` |
 | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
 | `statistics` | boolean | no | Include group statistics (admins only) |
 | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
-| `owned` | boolean | no | Limit to groups owned by the current user |
+| `owned` | boolean | no | Limit to groups explicitly owned by the current user |
+| `min_access_level` | integer | no | Limit to groups where current user has at least this [access level](members.md) |
 
 ```
 GET /groups/:id/subgroups
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 5613cb6d9154482dfaea41dd6a2b3976681830c4..92fb3e9c307e39b90a1c0e608e1c5e9ca5d84673 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -37,7 +37,7 @@ GET /issues?my_reaction_emoji=star
 | ------------------- | ---------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
 | `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. `No+Label` lists all issues with no labels                         |
-| `milestone`         | string           | no         | The milestone title                                                                                                                                 |
+| `milestone`         | string           | no         | The milestone title. `No+Milestone` lists all issues with no milestone                                                                                                                                |
 | `scope`             | string           | no         | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
 | `author_id`         | integer          | no         | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_          |
 | `assignee_id`       | integer          | no         | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_                                                              |
@@ -151,7 +151,7 @@ GET /groups/:id/issues?my_reaction_emoji=star
 | `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. `No+Label` lists all issues with no labels   |
 | `iids[]`            | Array[integer]   | no         | Return only the issues having the given `iid`                                                                                 |
-| `milestone`         | string           | no         | The milestone title                                                                                                           |
+| `milestone`         | string           | no         | The milestone title. `No+Milestone` lists all issues with no milestone                                                                                                           |
 | `scope`             | string           | no         | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
 | `author_id`         | integer          | no         | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_                                         |
 | `assignee_id`       | integer          | no         | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_                                        |
@@ -265,7 +265,7 @@ GET /projects/:id/issues?my_reaction_emoji=star
 | `iids[]`            | Array[integer]   | no         | Return only the milestone 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, issues must have all labels to be returned. `No+Label` lists all issues with no labels   |
-| `milestone`         | string           | no         | The milestone title                                                                                                           |
+| `milestone`         | string           | no         | The milestone title. `No+Milestone` lists all issues with no milestone                                                                                                           |
 | `scope`             | string           | no         | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.<br> For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.<br> _([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ |
 | `author_id`         | integer          | no         | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_                                         |
 | `assignee_id`       | integer          | no         | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_                                        |
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index cfa5e9a3e95f369fb5cf538db7afb04d794f6515..9a950097675ea4cb836654e6584cb12f7281e6d7 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -50,6 +50,7 @@ Example of response
     "started_at": "2015-12-24T17:54:24.729Z",
     "status": "failed",
     "tag": false,
+    "web_url": "https://example.com/foo/bar/-/jobs/6",
     "user": {
       "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
       "bio": null,
@@ -82,7 +83,7 @@ Example of response
       "size": 1000
     },
     "finished_at": "2015-12-24T17:54:27.895Z",
-    "artifacts_expire_at": "2016-01-23T17:54:27.895Z"
+    "artifacts_expire_at": "2016-01-23T17:54:27.895Z",
     "id": 7,
     "name": "teaspoon",
     "pipeline": {
@@ -97,6 +98,7 @@ Example of response
     "started_at": "2015-12-24T17:54:27.722Z",
     "status": "failed",
     "tag": false,
+    "web_url": "https://example.com/foo/bar/-/jobs/7",
     "user": {
       "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
       "bio": null,
@@ -151,7 +153,7 @@ Example of response
     "created_at": "2015-12-24T15:51:21.727Z",
     "artifacts_file": null,
     "finished_at": "2015-12-24T17:54:24.921Z",
-    "artifacts_expire_at": "2016-01-23T17:54:24.921Z"
+    "artifacts_expire_at": "2016-01-23T17:54:24.921Z",
     "id": 6,
     "name": "rspec:other",
     "pipeline": {
@@ -166,6 +168,7 @@ Example of response
     "started_at": "2015-12-24T17:54:24.729Z",
     "status": "failed",
     "tag": false,
+    "web_url": "https://example.com/foo/bar/-/jobs/6",
     "user": {
       "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
       "bio": null,
@@ -198,7 +201,7 @@ Example of response
       "size": 1000
     },
     "finished_at": "2015-12-24T17:54:27.895Z",
-    "artifacts_expire_at": "2016-01-23T17:54:27.895Z"
+    "artifacts_expire_at": "2016-01-23T17:54:27.895Z",
     "id": 7,
     "name": "teaspoon",
     "pipeline": {
@@ -213,6 +216,7 @@ Example of response
     "started_at": "2015-12-24T17:54:27.722Z",
     "status": "failed",
     "tag": false,
+    "web_url": "https://example.com/foo/bar/-/jobs/7",
     "user": {
       "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
       "bio": null,
@@ -280,6 +284,7 @@ Example of response
   "started_at": "2015-12-24T17:54:30.733Z",
   "status": "failed",
   "tag": false,
+  "web_url": "https://example.com/foo/bar/-/jobs/8",
   "user": {
     "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
     "bio": null,
@@ -455,7 +460,7 @@ Example of response
   "created_at": "2016-01-11T10:13:33.506Z",
   "artifacts_file": null,
   "finished_at": "2016-01-11T10:14:09.526Z",
-  "id": 69,
+  "id": 42,
   "name": "rubocop",
   "ref": "master",
   "runner": null,
@@ -463,6 +468,7 @@ Example of response
   "started_at": null,
   "status": "canceled",
   "tag": false,
+  "web_url": "https://example.com/foo/bar/-/jobs/42",
   "user": null
 }
 ```
@@ -501,7 +507,7 @@ Example of response
   "created_at": "2016-01-11T10:13:33.506Z",
   "artifacts_file": null,
   "finished_at": null,
-  "id": 69,
+  "id": 42,
   "name": "rubocop",
   "ref": "master",
   "runner": null,
@@ -509,6 +515,7 @@ Example of response
   "started_at": null,
   "status": "pending",
   "tag": false,
+  "web_url": "https://example.com/foo/bar/-/jobs/42",
   "user": null
 }
 ```
@@ -549,7 +556,7 @@ Example of response
   },
   "coverage": null,
   "download_url": null,
-  "id": 69,
+  "id": 42,
   "name": "rubocop",
   "ref": "master",
   "runner": null,
@@ -559,6 +566,7 @@ Example of response
   "finished_at": "2016-01-11T10:15:10.506Z",
   "status": "failed",
   "tag": false,
+  "web_url": "https://example.com/foo/bar/-/jobs/42",
   "user": null
 }
 ```
@@ -599,7 +607,7 @@ Example response:
   },
   "coverage": null,
   "download_url": null,
-  "id": 69,
+  "id": 42,
   "name": "rubocop",
   "ref": "master",
   "runner": null,
@@ -609,6 +617,7 @@ Example response:
   "finished_at": "2016-01-11T10:15:10.506Z",
   "status": "failed",
   "tag": false,
+  "web_url": "https://example.com/foo/bar/-/jobs/42",
   "user": null
 }
 ```
@@ -647,7 +656,7 @@ Example of response
   "created_at": "2016-01-11T10:13:33.506Z",
   "artifacts_file": null,
   "finished_at": null,
-  "id": 69,
+  "id": 42,
   "name": "rubocop",
   "ref": "master",
   "runner": null,
@@ -655,6 +664,7 @@ Example of response
   "started_at": null,
   "status": "started",
   "tag": false,
+  "web_url": "https://example.com/foo/bar/-/jobs/42",
   "user": null
 }
 ```
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index 22cf9afbcd2f6ce36fde91c2d8e490a8ceaf153e..574be52801ce5e30b2c71ae46c1a338075baed43 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -33,13 +33,15 @@ Example of response
     "id": 47,
     "status": "pending",
     "ref": "new-pipeline",
-    "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a"
+    "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
+    "web_url": "https://example.com/foo/bar/pipelines/47"
   },
   {
     "id": 48,
     "status": "pending",
     "ref": "new-pipeline",
-    "sha": "eb94b618fb5865b26e80fdd8ae531b7a63ad851a"
+    "sha": "eb94b618fb5865b26e80fdd8ae531b7a63ad851a",
+    "web_url": "https://example.com/foo/bar/pipelines/48"
   }
 ]
 ```
@@ -86,7 +88,8 @@ Example of response
   "finished_at": "2016-08-11T11:32:35.145Z",
   "committed_at": null,
   "duration": null,
-  "coverage": "30.0"
+  "coverage": "30.0",
+  "web_url": "https://example.com/foo/bar/pipelines/46"
 }
 ```
 
@@ -133,7 +136,8 @@ Example of response
   "finished_at": null,
   "committed_at": null,
   "duration": null,
-  "coverage": null
+  "coverage": null,
+  "web_url": "https://example.com/foo/bar/pipelines/61"
 }
 ```
 
@@ -179,7 +183,8 @@ Response:
   "finished_at": "2016-08-11T11:32:35.145Z",
   "committed_at": null,
   "duration": null,
-  "coverage": null
+  "coverage": null,
+  "web_url": "https://example.com/foo/bar/pipelines/46"
 }
 ```
 
@@ -225,7 +230,8 @@ Response:
   "finished_at": "2016-08-11T11:32:35.145Z",
   "committed_at": null,
   "duration": null,
-  "coverage": null
+  "coverage": null,
+  "web_url": "https://example.com/foo/bar/pipelines/46"
 }
 ```
 
diff --git a/doc/api/projects.md b/doc/api/projects.md
index f3ccca46420b16c839fa2eef1f32d518a8b2deb1..f360b49c293e544ad9542c402ad0fd3e76b59b7f 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -48,7 +48,7 @@ GET /projects
 | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
 | `search` | string | no | Return list of projects matching the search criteria |
 | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
 | `membership` | boolean | no | Limit by projects that the current user is a member of |
 | `starred` | boolean | no | Limit by projects starred by the current user |
 | `statistics` | boolean | no | Include project statistics |
@@ -57,6 +57,7 @@ GET /projects
 | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
 | `wiki_checksum_failed` | boolean | no | Limit projects where the wiki checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ |
 | `repository_checksum_failed` | boolean | no | Limit projects where the repository checksum calculation has failed _([Introduced][ee-6137] in [GitLab Premium][eep] 11.2)_ |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
 
 When `simple=true` or the user is unauthenticated this returns something like:
 
@@ -273,13 +274,14 @@ GET /users/:user_id/projects
 | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
 | `search` | string | no | Return list of projects matching the search criteria |
 | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
 | `membership` | boolean | no | Limit by projects that the current user is a member of |
 | `starred` | boolean | no | Limit by projects starred by the current user |
 | `statistics` | boolean | no | Include project statistics |
 | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
 | `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
 | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
 
 ```json
 [
@@ -573,7 +575,15 @@ If the project is a fork, and you provide a valid token to authenticate, the
       "avatar_url":"https://assets.gitlab-static.net/uploads/-/system/project/avatar/13083/logo-extra-whitespace.png",
       "star_count":3812,
       "forks_count":3561,
-      "last_activity_at":"2018-01-02T11:40:26.570Z"
+      "last_activity_at":"2018-01-02T11:40:26.570Z",
+      "namespace": {
+            "id": 72,
+            "name": "GitLab.org",
+            "path": "gitlab-org",
+            "kind": "group",
+            "full_path": "gitlab-org",
+            "parent_id": null
+      }
    }
 
    ...
@@ -761,13 +771,14 @@ GET /projects/:id/forks
 | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
 | `search` | string | no | Return list of projects matching the search criteria |
 | `simple` | boolean | no | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
-| `owned` | boolean | no | Limit by projects owned by the current user |
+| `owned` | boolean | no | Limit by projects explicitly owned by the current user |
 | `membership` | boolean | no | Limit by projects that the current user is a member of |
 | `starred` | boolean | no | Limit by projects starred by the current user |
 | `statistics` | boolean | no | Include project statistics |
 | `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
 | `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
 | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
 
 ```bash
 curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/forks"
diff --git a/doc/api/services.md b/doc/api/services.md
index aeb48ccc36c3c6c3984b1d8429619c14d9b48203..efa173180bb43ca01698081a73654be3bc58e1f8 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -443,6 +443,54 @@ Get Gemnasium service settings for a project.
 GET /projects/:id/services/gemnasium
 ```
 
+## Hangouts Chat
+
+Google GSuite team collaboration tool.
+
+>**Note:** This service was [introduced in v11.2](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20290)
+
+### Create/Edit Hangouts Chat service
+
+Set Hangouts Chat service for a project.
+
+```
+PUT /projects/:id/services/hangouts_chat
+```
+
+>**Note:** Specific event parameters (e.g. `push_events` flag) were [introduced in v10.4][11435]
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces... |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | Send notifications only for the default branch |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Hangouts Chat service
+
+Delete Hangouts Chat service for a project.
+
+```
+DELETE /projects/:id/services/hangouts_chat
+```
+
+### Get Hangouts Chat service settings
+
+Get Hangouts Chat service settings for a project.
+
+```
+GET /projects/:id/services/hangouts_chat
+```
+
 ## HipChat
 
 Private group chat and IM
diff --git a/doc/api/users.md b/doc/api/users.md
index 72fdaaa2c74faa7efc300a137871ee122779ad1f..07f9baf06d295986e14ccc4443ff4e014318c300 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -105,7 +105,8 @@ GET /users
     "can_create_group": true,
     "can_create_project": true,
     "two_factor_enabled": true,
-    "external": false
+    "external": false,
+    "private_profile": false
   },
   {
     "id": 2,
@@ -135,7 +136,8 @@ GET /users
     "can_create_group": true,
     "can_create_project": true,
     "two_factor_enabled": true,
-    "external": false
+    "external": false,
+    "private_profile": false
   }
 ]
 ```
@@ -248,7 +250,8 @@ Parameters:
   "can_create_group": true,
   "can_create_project": true,
   "two_factor_enabled": true,
-  "external": false
+  "external": false,
+  "private_profile": false
 }
 ```
 
@@ -288,6 +291,7 @@ Parameters:
 - `skip_confirmation` (optional) - Skip confirmation - true or false (default)
 - `external` (optional)          - Flags the user as external - true or false(default)
 - `avatar` (optional)            - Image file for user's avatar
+- `private_profile` (optional)   - User's profile is private - true or false
 
 ## User modification
 
@@ -318,6 +322,7 @@ Parameters:
 - `skip_reconfirmation` (optional) - Skip reconfirmation - true or false (default)
 - `external` (optional)         - Flags the user as external - true or false(default)
 - `avatar` (optional)           - Image file for user's avatar
+- `private_profile` (optional)  - User's profile is private - true or false
 
 On password update, user will be forced to change it upon next login.
 Note, at the moment this method does only return a `404` error,
@@ -382,7 +387,8 @@ GET /user
   "can_create_group": true,
   "can_create_project": true,
   "two_factor_enabled": true,
-  "external": false
+  "external": false,
+  "private_profile": false
 }
 ```
 
@@ -429,7 +435,8 @@ GET /user
   "can_create_group": true,
   "can_create_project": true,
   "two_factor_enabled": true,
-  "external": false
+  "external": false,
+  "private_profile": false
 }
 ```
 
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index fbac37e688e2c798bebe32e84ddc166355e71d83..63338ff632cbaa49206907e5381397a487106a13 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -427,7 +427,7 @@ services:
 variables:
   DOCKER_HOST: tcp://docker:2375
   DOCKER_DRIVER: overlay2
-  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME
+  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
 
 before_script:
   - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
@@ -440,8 +440,10 @@ build:
 ```
 
 Here, `$CI_REGISTRY_IMAGE` would be resolved to the address of the registry tied
-to this project, and `$CI_COMMIT_REF_NAME` would be resolved to the branch or
-tag name for this particular job. We also declare our own variable, `$IMAGE_TAG`,
+to this project. Since `$CI_COMMIT_REF_NAME` resolves to the branch or tag name,
+and your branch-name can contain forward slashes (e.g., feature/my-feature), it is
+safer to use `$CI_COMMIT_REF_SLUG` as the image tag. This is due to that image tags
+cannot contain forward slashes. We also declare our own variable, `$IMAGE_TAG`,
 combining the two to save us some typing in the `script` section.
 
 Here's a more elaborate example that splits up the tasks into 4 pipeline stages,
@@ -464,7 +466,7 @@ stages:
 variables:
   DOCKER_HOST: tcp://docker:2375
   DOCKER_DRIVER: overlay2
-  CONTAINER_TEST_IMAGE: registry.example.com/my-group/my-project/my-image:$CI_COMMIT_REF_NAME
+  CONTAINER_TEST_IMAGE: registry.example.com/my-group/my-project/my-image:$CI_COMMIT_REF_SLUG
   CONTAINER_RELEASE_IMAGE: registry.example.com/my-group/my-project/my-image:latest
 
 before_script:
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 84bd64d50cd3dc244529a1606b2e90b0455e8052..115e6e390c956384bded9e396b9bb21c0ebe07f5 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -64,7 +64,7 @@ future GitLab releases.**
 | **CI_JOB_MANUAL**               | 8.12   | all    | The flag to indicate that job was manually started |
 | **CI_JOB_NAME**                 | 9.0    | 0.5    | The name of the job as defined in `.gitlab-ci.yml` |
 | **CI_JOB_STAGE**                | 9.0    | 0.5    | The name of the stage as defined in `.gitlab-ci.yml` |
-| **CI_JOB_TOKEN**                | 9.0    | 1.2    | Token used for authenticating with the GitLab Container Registry |
+| **CI_JOB_TOKEN**                | 9.0    | 1.2    | Token used for authenticating with the [GitLab Container Registry][registry] and downloading [dependent repositories][dependent-repositories] |
 | **CI_JOB_URL**                  | 11.1   | 0.5    | Job details URL |
 | **CI_REPOSITORY_URL**           | 9.0    | all    | The URL to clone the Git repository |
 | **CI_RUNNER_DESCRIPTION**       | 8.10   | 0.5    | The description of the runner as saved in GitLab |
@@ -566,3 +566,5 @@ Below you can find supported syntax reference:
 [subgroups]: ../../user/group/subgroups/index.md
 [builds-policies]: ../yaml/README.md#only-and-except-complex
 [gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token
+[registry]: ../../user/project/container_registry.md
+[dependent-repositories]: ../../user/project/new_ci_build_permissions_model.md#dependent-repositories
\ No newline at end of file
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 096b64eb8810a8103bc553571bf4f32764e6d486..d95f8c7c8ccb2c9fd86926597bd8e23771af8e35 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1411,43 +1411,6 @@ variables:
 
 You can set it globally or per-job in the [`variables`](#variables) section.
 
-### Custom build directories
-
-> [Introduced][gitlab-runner-876] in Gitlab Runner 11.1
-
-NOTE: **Note:**
-This can only be used when `custom_build_dir` is set to true in the [Runner's
-configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html).
-
-By default, GitLab Runner clones the repository in the `/builds` directory,
-but sometimes your project might require to have the code in a specific
-directory, like the GO projects for example. In that case, you can specify
-the `CI_PROJECT_DIR` variable to tell the Runner in which directory to clone
-the repository:
-
-```yml
-image: golang:1.10-alpine3.7
-
-variables:
-  CI_PROJECT_DIR: /go/src/gitlab.com/namespace/project-name
-
-stages:
-    - test
-
-dir:
-    stage: test
-    script:
-      - pwd # /go/src/gitlab.com/namespace/project-name
-```
-
-The following executors may use this feature only when
-[concurrent](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section)
-is set to `1`:
-
-- `shell`
-- `ssh`
-- `docker`, `docker+machine` when the job's working directory is mounted as a host volume.
-
 ## Special YAML features
 
 It's possible to use special YAML features like anchors (`&`), aliases (`*`)
@@ -1641,6 +1604,5 @@ CI with various languages.
 [ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983
 [ce-7447]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7447
 [ce-12909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12909
-[gitlab-runner-876]: https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/876
 [schedules]: ../../user/project/pipelines/schedules.md
 [variables-expressions]: ../variables/README.md#variables-expressions
diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md
index 46c5baddb9cbc12e7873b93d1712628ae41e2408..16195cbbbdf119d2cb921ab22b20afe41014ac7e 100644
--- a/doc/development/background_migrations.md
+++ b/doc/development/background_migrations.md
@@ -296,10 +296,18 @@ for more details.
 
 ## Best practices
 
+1. Make sure to know how much data you're dealing with
 1. Make sure that background migration jobs are idempotent.
 1. Make sure that tests you write are not false positives.
 1. Make sure that if the data being migrated is critical and cannot be lost, the
    clean-up migration also checks the final state of the data before completing.
+1. Make sure to know how much time it'll take to run all scheduled migrations
+1. When migrating many columns, make sure it won't generate too many
+   dead tuples in the process (you may need to directly query the number of dead tuples
+   and adjust the scheduling according to this piece of data)
+1. Make sure to discuss the numbers with a database specialist, the migration may add
+   more pressure on DB than you expect (measure on staging,
+   or ask someone to measure on production)
 
 [migrations-readme]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/migrations/README.md
 [issue-rspec-hooks]: https://gitlab.com/gitlab-org/gitlab-ce/issues/35351
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index a315cfdc11643b659d7b7a59987d49f37ad7d541..ad49c77aac8094259e52b75b5f1bb893c5596196 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -219,28 +219,26 @@ below.
 
 - Every piece of documentation that comes with a new feature should declare the
   GitLab version that feature got introduced. Right below the heading add a
-  note:
+  blockquote:
 
     ```md
     > Introduced in GitLab 8.3.
     ```
 
-- Whenever possible, every feature should have a link to the MR, issue, or epic that introduced it.
-  The above note would be then transformed to:
+- Whenever possible, every feature should have a link to the issue, MR or epic
+  (in that order) that introduced it. The above quote would be then transformed to:
 
     ```md
-    > [Introduced][ce-1242] in GitLab 8.3.
+    > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/1242) in GitLab 8.3.
     ```
 
-    , where the [link identifier](#links) is named after the repository (CE) and
-    the MR number.
-
 - If the feature is only available in GitLab Enterprise Edition, don't forget to mention
   the [paid tier](https://about.gitlab.com/handbook/marketing/product-marketing/#tiers)
   the feature is available in:
 
     ```md
-    > [Introduced][ee-1234] in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
+    > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/1242)
+    in [GitLab Starter](https://about.gitlab.com/pricing/) 8.3.
     ```
 
 ### Product badges
diff --git a/doc/install/kubernetes/preparation/tiller.md b/doc/install/kubernetes/preparation/tiller.md
index c92f8258e41d7c22813be05fdb098b4efed9a640..016aac2abebd7756aed384ac46194c5ba91b139b 100644
--- a/doc/install/kubernetes/preparation/tiller.md
+++ b/doc/install/kubernetes/preparation/tiller.md
@@ -4,7 +4,7 @@ To make use of Helm, you must have a [Kubernetes][k8s-io] cluster. Ensure you ca
 
 Helm consists of two parts, the `helm` client and a `tiller` server inside Kubernetes.
 
-> **Note**: If you are not able to run tiller in your cluster, for example on OpenShift, it is possible to use [tiller locally](#local-tiller) and avoid deploying it into the cluster. This should only be used when Tiller cannot be normally deployed.
+> **Note**: If you are not able to run Tiller in your cluster, for example on OpenShift, it is possible to use [Tiller locally](https://gitlab.com/charts/gitlab/tree/master/doc/helm#local-tiller) and avoid deploying it into the cluster. This should only be used when Tiller cannot be normally deployed.
 
 ## Initialize Helm and Tiller
 
@@ -65,28 +65,32 @@ kubectl --username=admin --password=xxxxxxxxxxxxxx create -f rbac-config.yaml
 
 For other clusters like Amazon EKS, you can directly upload the RBAC configuration.
 
+```
 kubectl create -f rbac-config.yaml
+```
 
 ## Initialize Helm
 
-Deploy Helm Tiller with a service account
+Deploy Helm Tiller with a service account:
 
 ```
 helm init --service-account tiller
 ```
 
-If your cluster
-previously had Helm/Tiller installed, run the following to ensure that the deployed version of Tiller matches the local Helm version:
+If your cluster previously had Helm/Tiller installed,
+run the following to ensure that the deployed version of Tiller matches the local Helm version:
 
 ```
 helm init --upgrade --service-account tiller
 ```
 
-### Patching Helm Tiller for EKS
+### Patching Helm Tiller for Amazon EKS
 
-Helm Tiller requires a flag to be enabled to work properly on EKS:
+Helm Tiller requires a flag to be enabled to work properly on Amazon EKS:
 
-`kubectl -n kube-system patch deployment tiller-deploy -p '{"spec": {"template": {"spec": {"automountServiceAccountToken": true}}}}'`
+```
+kubectl -n kube-system patch deployment tiller-deploy -p '{"spec": {"template": {"spec": {"automountServiceAccountToken": true}}}}'
+```
 
 [helm]: https://helm.sh
 [helm-using]: https://docs.helm.sh/using_helm
diff --git a/doc/security/rack_attack.md b/doc/security/rack_attack.md
index c61729581e8d103ff10de5b57308492aa0c8febc..3efb19c1526f1530218e50de7e05c2a7034d64c4 100644
--- a/doc/security/rack_attack.md
+++ b/doc/security/rack_attack.md
@@ -9,13 +9,17 @@ In case you find throttling is not enough to protect you against abusive clients
 Rack Attack offers IP whitelisting, blacklisting, Fail2ban style filtering and
 tracking.
 
+**Note:** Starting with 11.2, Rack Attack is disabled by default. To continue
+using this feature, please enable it in your `gitlab.rb` by setting 
+`gitlab_rails['rack_attack_git_basic_auth'] = true`.
+
 By default, user sign-in, user sign-up (if enabled), and user password reset is
 limited to 6 requests per minute. After trying for 6 times, the client will
 have to wait for the next minute to be able to try again.
 
 If you installed or upgraded GitLab by following the [official guides](../install/README.md)
-this should be enabled by default. If your instance is not exposed to any incoming
-connections, it is recommended to disable Rack Attack.
+this should be disabled by default. If your instance is not exposed to any incoming
+connections, it is recommended to leave Rack Attack disabled.
 
 For more information on how to use these options check out
 [rack-attack README](https://github.com/kickstarter/rack-attack/blob/master/README.md).
@@ -45,7 +49,7 @@ For more information on how to use these options check out
 
 The following settings can be configured:
 
-- `enabled`: By default this is set to `true`. Set this to `false` to disable Rack Attack.
+- `enabled`: By default this is set to `false`. Set this to `true` to enable Rack Attack.
 - `ip_whitelist`: Whitelist any IPs from being blocked. They must be formatted as strings within a ruby array.
    For example, `["127.0.0.1", "127.0.0.2", "127.0.0.3"]`.
 - `maxretry`: The maximum amount of times a request can be made in the
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index de1d366adc31f0b779e5b4b007bda13eb62023b3..f5574506595e7b3b6b8974926bfc01f2da2f8190 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -591,7 +591,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
 | `CANARY_ENABLED`             | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
 | `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. |
 | `TEST_DISABLED`              | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
-| `CODEQUALITY_DISABLED`       | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
+| `CODE_QUALITY_DISABLED`       | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
 | `SAST_DISABLED`              | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
 | `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. |
 | `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. |
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 0b38cde811d0fb2c0c2eab44d0757f3a5154a0c4..7c6a14cb104a6c66560db405d45968a45a984a3e 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -200,6 +200,12 @@ sudo apt-get install pgloader
     sudo -u git cp config/database.yml.postgresql config/database.yml
     sudo -u git -H chmod o-rwx config/database.yml
     ```
+1. Install Gems related to Postgresql
+
+    ``` bash
+    sudo -u git -H rm .bundle/config
+    sudo -u git -H bundle install --deployment --without development test mysql aws kerberos
+    ```
 
 1. Run the following commands to prepare the schema:
 
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 91cdef8d1dd79f2e9f87be76620e7ac790b8fbe6..96a08c04905a5315d64af588e1fff0bba7d7100c 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -68,6 +68,28 @@ Alternatively, you can follow [this detailed procedure from the GitLab Team Hand
 which also covers the case where you have projects hosted with
 [GitLab Pages](../project/pages/index.md).
 
+## Private profile
+
+The following information will be hidden from the user profile page (https://gitlab.example.com/username) if this feature is enabled:
+
+- Atom feed
+- Date when account is created
+- Activity tab
+- Groups tab
+- Contributed projects tab
+- Personal projects tab
+- Snippets tab
+
+To enable private profile:
+
+1. Navigate to your personal [profile settings](#profile-settings).
+1. Check the "Private profile" option.
+1. Hit **Update profile settings**.
+
+
+NOTE: **Note:**
+You and GitLab admins can see your the abovementioned information on your profile even if it is private.
+
 ## Troubleshooting
 
 ### Why do I keep getting signed out?
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
index 9b4fdd65e2f785cea42ff8ad26c04c531a79a24e..25d6c34409c245151e25c536f8ab164c822f6d0c 100644
--- a/doc/user/profile/personal_access_tokens.md
+++ b/doc/user/profile/personal_access_tokens.md
@@ -48,6 +48,7 @@ the following table.
 | `api` | Grants complete access to the API (read/write) ([introduced][ce-5951] in GitLab 8.15). Required for accessing Git repositories over HTTP when 2FA is enabled. |
 | `read_registry` | Allows to read [container registry] images if a project is private and authorization is required ([introduced][ce-11845] in GitLab 9.3). |
 | `sudo` | Allows performing API actions as any user in the system (if the authenticated user is an admin) ([introduced][ce-14838] in GitLab 10.2). |
+| `read_repository` | Allows read-access to the repository through git clone. |
 
 [2fa]: ../account/two_factor_authentication.md
 [api]: ../../api/README.md
diff --git a/doc/user/project/integrations/hangouts_chat.md b/doc/user/project/integrations/hangouts_chat.md
new file mode 100644
index 0000000000000000000000000000000000000000..6ab44420a10472c4305ff8f5cd1e4329fed65a64
--- /dev/null
+++ b/doc/user/project/integrations/hangouts_chat.md
@@ -0,0 +1,27 @@
+# Hangouts Chat service
+
+The Hangouts Chat service sends notifications from GitLab to the room for which the webhook was created.
+
+## On Hangouts Chat
+
+1. Open the chat room in which you want to see the notifications.
+1. From the chat room menu, select **Configure Webhooks**.
+1. Click on **ADD WEBHOOK** and fill in the name of the bot that will post the messages. Optionally define avatar.
+1. Click **SAVE** and copy the **Webhook URL** of your webhook.
+
+See also [the Hangouts Chat documentation for configuring incoming webhooks](https://developers.google.com/hangouts/chat/how-tos/webhooks)
+
+## On GitLab
+
+When you have the **Webhook URL** for your Hangouts Chat room webhook, you can setup the GitLab service.
+
+1. Navigate to the [Integrations page](project_services.md#accessing-the-project-services) in your project's settings, i.e. **Project > Settings > Integrations**.
+1. Select the **Hangouts Chat** project service to configure it.
+1. Check the **Active** checkbox to turn on the service.
+1. Check the checkboxes corresponding to the GitLab events you want to receive.
+1. Paste the **Webhook URL** that you copied from the Hangouts Chat configuration step.
+1. Configure the remaining options and click `Save changes`.
+
+Your Hangouts Chat room will now start receiving GitLab event notifications as configured.
+
+![Hangouts Chat configuration](img/hangouts_chat_configuration.png)
diff --git a/doc/user/project/integrations/img/hangouts_chat_configuration.png b/doc/user/project/integrations/img/hangouts_chat_configuration.png
new file mode 100644
index 0000000000000000000000000000000000000000..33fadbe6547e314d021aa83ba797a55474c96067
Binary files /dev/null and b/doc/user/project/integrations/img/hangouts_chat_configuration.png differ
diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md
index 8c51eb9915e1c0f84721efd6431a0d9db2e36279..05ee1b4e6d7c3b4672c87489a782174efc941006 100644
--- a/doc/user/project/integrations/project_services.md
+++ b/doc/user/project/integrations/project_services.md
@@ -35,6 +35,7 @@ Click on the service links to see further configuration instructions and details
 | External Wiki | Replaces the link to the internal wiki with a link to an external wiki |
 | Flowdock | Flowdock is a collaboration web app for technical teams |
 | Gemnasium   _(Has been deprecated in GitLab 11.0)_ | Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities |
+| [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat |
 | [HipChat](hipchat.md) | Private group chat and IM |
 | [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway |
 | [JIRA](jira.md) | JIRA issue tracker |
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 860edb8e6f76e85b6ed1785000e929dd3e97067e..6dfdbe6c0d55d26ca5323c798575b3c11eca14f2 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -27,7 +27,7 @@ You create issues, host code, perform reviews, build, test,
 and deploy from one single platform. Issue Boards help you to visualize
 and manage the entire process _in_ GitLab.
 
-With [Multiple Issue Boards](#multiple-issue-boards), available
+With [Multiple Issue Boards](#use-cases-for-multiple-issue-boards), available
 only in [GitLab Enterprise Edition](#features-per-tier),
 you go even further, as you can not only keep yourself and your project
 organized from a broader perspective with one Issue Board per project,
@@ -75,7 +75,7 @@ each team can have their own board to organize their workflow individually.
 
 #### Scrum team
 
-With multiple Issue Boards, each team has one board. Now you can move issues through each
+With Multiple Issue Boards, each team has one board. Now you can move issues through each
 part of the process. For instance: **To Do**, **Doing**, and **Done**.
 
 #### Organization of topics
diff --git a/doc/user/project/merge_requests/img/merge_request.png b/doc/user/project/merge_requests/img/merge_request.png
index f9ca63489531555646c5d079cdf3901b9c69f171..61b61122b112de09cc3a7a231f92ce9bead88b93 100644
Binary files a/doc/user/project/merge_requests/img/merge_request.png and b/doc/user/project/merge_requests/img/merge_request.png differ
diff --git a/doc/user/project/milestones/img/milestones_new_group_milestone.png b/doc/user/project/milestones/img/milestones_new_group_milestone.png
index 8780394d72e6baa6d0d6b4f770e9e667d9305e6b..b6defab101d470d5a319e673799f467b44010c7b 100644
Binary files a/doc/user/project/milestones/img/milestones_new_group_milestone.png and b/doc/user/project/milestones/img/milestones_new_group_milestone.png differ
diff --git a/doc/user/project/milestones/img/milestones_new_project_milestone.png b/doc/user/project/milestones/img/milestones_new_project_milestone.png
index ba058428dfafbd67f9aed6d3e977e647ec071da5..9aaff7dfef1b6a567ba1db729b5d91cd4a2bf526 100644
Binary files a/doc/user/project/milestones/img/milestones_new_project_milestone.png and b/doc/user/project/milestones/img/milestones_new_project_milestone.png differ
diff --git a/doc/user/project/milestones/img/milestones_promote_milestone.png b/doc/user/project/milestones/img/milestones_promote_milestone.png
index 99bee1240d4ba2ba1baf2362e2ebf778790638de..5e7f94c316fae03da9313478823986ea02d8aa3d 100644
Binary files a/doc/user/project/milestones/img/milestones_promote_milestone.png and b/doc/user/project/milestones/img/milestones_promote_milestone.png differ
diff --git a/generator_templates/active_record/migration/create_table_migration.rb b/generator_templates/active_record/migration/create_table_migration.rb
index 59a9d37df0fc42348083fbd3a5b7fd35b3fbc826..92e963911d0a9885e9356e21447df81e8c75c3f4 100644
--- a/generator_templates/active_record/migration/create_table_migration.rb
+++ b/generator_templates/active_record/migration/create_table_migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # See http://doc.gitlab.com/ce/development/migration_style_guide.html
 # for more information on how to write migrations for GitLab.
 
diff --git a/generator_templates/active_record/migration/migration.rb b/generator_templates/active_record/migration/migration.rb
index 08752b3af505756ee45f90a197f82c7fd4f84c69..38edab825509f209e232b890c9632bf69401ef7c 100644
--- a/generator_templates/active_record/migration/migration.rb
+++ b/generator_templates/active_record/migration/migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # See http://doc.gitlab.com/ce/development/migration_style_guide.html
 # for more information on how to write migrations for GitLab.
 
diff --git a/generator_templates/rails/post_deployment_migration/migration.rb b/generator_templates/rails/post_deployment_migration/migration.rb
index f2dff84b61837da93d0b75d6e0bbbf4c0613b3b3..353709f7c9ca1ca6699121fdcc6167791d069dc2 100644
--- a/generator_templates/rails/post_deployment_migration/migration.rb
+++ b/generator_templates/rails/post_deployment_migration/migration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # See http://doc.gitlab.com/ce/development/migration_style_guide.html
 # for more information on how to write migrations for GitLab.
 
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 3f3a95ea8e6e235a16a85d0a6cbb765d983eee00..e883687f2dba4738deb0a8bec5b89f75f20a4a20 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -30,7 +30,7 @@ module API
     end
 
     class User < UserBasic
-      expose :created_at
+      expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) }
       expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization
     end
 
@@ -55,6 +55,7 @@ module API
       expose :can_create_project?, as: :can_create_project
       expose :two_factor_enabled?, as: :two_factor_enabled
       expose :external
+      expose :private_profile
     end
 
     class UserWithAdmin < UserPublic
@@ -132,6 +133,7 @@ module API
       expose :star_count, :forks_count
       expose :last_activity_at
 
+      expose :namespace, using: 'API::Entities::NamespaceBasic'
       expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
 
       def self.preload_relation(projects_relation, options =  {})
@@ -194,7 +196,6 @@ module API
       expose :shared_runners_enabled
       expose :lfs_enabled?, as: :lfs_enabled
       expose :creator_id
-      expose :namespace, using: 'API::Entities::NamespaceBasic'
       expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? }
       expose :import_status
       expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] }
@@ -529,6 +530,10 @@ module API
 
     class PipelineBasic < Grape::Entity
       expose :id, :sha, :ref, :status
+
+      expose :web_url do |pipeline, _options|
+        Gitlab::Routing.url_helpers.project_pipeline_url(pipeline.project, pipeline)
+      end
     end
 
     class MergeRequestSimple < ProjectEntity
@@ -1069,6 +1074,10 @@ module API
       expose :user, with: User
       expose :commit, with: Commit
       expose :pipeline, with: PipelineBasic
+
+      expose :web_url do |job, _options|
+        Gitlab::Routing.url_helpers.project_job_url(job.project, job)
+      end
     end
 
     class Job < JobBasic
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 797b04df059374a8758993a588e7388ab78fd9e6..b4f441f6a4fbcfd2928fc05377553feb9fd95c07 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -34,11 +34,12 @@ module API
         optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
         optional :order_by, type: String, values: %w[name path id], default: 'name', desc: 'Order by name, path or id'
         optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
+        optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Minimum access level of authenticated user'
         use :pagination
       end
 
       def find_groups(params, parent_id = nil)
-        find_params = params.slice(:all_available, :custom_attributes, :owned)
+        find_params = params.slice(:all_available, :custom_attributes, :owned, :min_access_level)
         find_params[:parent] = find_group!(parent_id) if parent_id
         find_params[:all_available] =
           find_params.fetch(:all_available, current_user&.full_private_access?)
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index f77374681488523b3471ea206493e1b636959c00..be17653dbb268fb3ccf72ac4d20013019832d65e 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -389,6 +389,7 @@ module API
       finder_params[:search] = params[:search] if params[:search]
       finder_params[:user] = params.delete(:user) if params[:user]
       finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes]
+      finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level]
       finder_params
     end
 
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index e95b0dd52670baec135bb4a0ec6be5288abf210d..10c6e565f091e2f4fa303632748c0d5c238d0e45 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -54,7 +54,7 @@ module API
         pipeline = user_project.pipelines.find(params[:pipeline_id])
         builds = pipeline.builds
         builds = filter_builds(builds, params[:scope])
-        builds = builds.preload(:job_artifacts_archive)
+        builds = builds.preload(:job_artifacts_archive, project: [:namespace])
 
         present paginate(builds), with: Entities::Job
       end
diff --git a/lib/api/keys.rb b/lib/api/keys.rb
index 767f27ef334122f129ede6bc6aaccc08cb7b5e0c..fd93f797f72bf71b87fd1ccc0ab751b7df0b94b0 100644
--- a/lib/api/keys.rb
+++ b/lib/api/keys.rb
@@ -12,7 +12,7 @@ module API
 
         key = Key.find(params[:id])
 
-        present key, with: Entities::SSHKeyWithUser
+        present key, with: Entities::SSHKeyWithUser, current_user: current_user
       end
     end
   end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 889e3d4f8197f52214a73904589204b6088de121..eadde7b17bb5c5741d9f035610c758ec71b008c0 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -54,6 +54,7 @@ module API
         optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of'
         optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature'
         optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature'
+        optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
 
         use :optional_filter_params_ee
       end
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 553e8dff4b970657bd6056ccebe380805bb0a81f..1f2bf546cd72b9861fec4607ad2daf7ad4f2a2c6 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -368,6 +368,14 @@ module API
           desc: "The project's slug on gemnasium.com"
         }
       ],
+      'hangouts-chat' => [
+        {
+          required: true,
+          name: :webhook,
+          type: String,
+          desc: 'The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces…'
+        }
+      ],
       'hipchat' => [
         {
           required: true,
@@ -688,6 +696,7 @@ module API
       ExternalWikiService,
       FlowdockService,
       GemnasiumService,
+      HangoutsChatService,
       HipchatService,
       IrkerService,
       JiraService,
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 5aaaf104dff7742d73b4c16f668ccca30aedd483..e83887b3e9eaa2f20a2f32fe7658d20706314686 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -42,6 +42,8 @@ module API
           optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
           optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
           optional :avatar, type: File, desc: 'Avatar image for user'
+          optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
+          optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
           all_or_none_of :extern_uid, :provider
         end
 
@@ -96,7 +98,7 @@ module API
 
         entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
         users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin
-        users, options = with_custom_attributes(users, with: entity)
+        users, options = with_custom_attributes(users, { with: entity, current_user: current_user })
 
         present paginate(users), options
       end
@@ -113,7 +115,7 @@ module API
         user = User.find_by(id: params[:id])
         not_found!('User') unless user && can?(current_user, :read_user, user)
 
-        opts = current_user&.admin? ? { with: Entities::UserWithAdmin } : { with: Entities::User }
+        opts = { with: current_user&.admin? ? Entities::UserWithAdmin : Entities::User, current_user: current_user }
         user, opts = with_custom_attributes(user, opts)
 
         present user, opts
@@ -139,7 +141,7 @@ module API
         user = ::Users::CreateService.new(current_user, params).execute(skip_authorization: true)
 
         if user.persisted?
-          present user, with: Entities::UserPublic
+          present user, with: Entities::UserPublic, current_user: current_user
         else
           conflict!('Email has already been taken') if User
               .where(email: user.email)
@@ -198,7 +200,7 @@ module API
         result = ::Users::UpdateService.new(current_user, user_params.except(:extern_uid, :provider).merge(user: user)).execute
 
         if result[:status] == :success
-          present user, with: Entities::UserPublic
+          present user, with: Entities::UserPublic, current_user: current_user
         else
           render_validation_error!(user)
         end
@@ -545,7 +547,7 @@ module API
               Entities::UserPublic
             end
 
-          present current_user, with: entity
+          present current_user, with: entity, current_user: current_user
         end
       end
 
diff --git a/lib/banzai/filter/absolute_link_filter.rb b/lib/banzai/filter/absolute_link_filter.rb
index 1ec6201523f15ba97e96b46b62be3e67398aff23..04ec568eee3085c1886a9888f1fd3647bcca18e4 100644
--- a/lib/banzai/filter/absolute_link_filter.rb
+++ b/lib/banzai/filter/absolute_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'uri'
 
 module Banzai
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index b39b11009b3dda1e320c4bd9ce1b752d6896b17c..ad0806df8e69ed63285aaa3cd21e31608cdfe75b 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # Issues, Merge Requests, Snippets, Commits and Commit Ranges share
diff --git a/lib/banzai/filter/ascii_doc_post_processing_filter.rb b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
index c9fcf057c5f41888646f6d3c400cfe9d59235e4b..88439f06b5f6b18fa0a0ba240335c2154df21222 100644
--- a/lib/banzai/filter/ascii_doc_post_processing_filter.rb
+++ b/lib/banzai/filter/ascii_doc_post_processing_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     class AsciiDocPostProcessingFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index 4a143baeef6bb6a990b859f1db6a5637b01a4b01..deda4b1872e149e1f0504b7055fdededd06213fb 100644
--- a/lib/banzai/filter/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'uri'
 
 module Banzai
diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
index 7108e828c6d06fb6b443f842b0f5c074e3471fe8..ad367cc5efe1306cc523893a1a52d20a6a709ab4 100644
--- a/lib/banzai/filter/blockquote_fence_filter.rb
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     class BlockquoteFenceFilter < HTML::Pipeline::TextFilter
diff --git a/lib/banzai/filter/color_filter.rb b/lib/banzai/filter/color_filter.rb
index 6ab29ac281f6cc94bd14633643fd3ee426784eba..6d9bdb9cbd3c108ce4effb6be2250c2160cbf165 100644
--- a/lib/banzai/filter/color_filter.rb
+++ b/lib/banzai/filter/color_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that renders `color` followed by a color "chip".
diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
index 01b3b0dafb9fedb4dfc042e5d584e6afd6f70f7e..d6b46236a49405256c78dc54b768f3dd3a58db2f 100644
--- a/lib/banzai/filter/commit_range_reference_filter.rb
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces commit range references with links.
diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
index 8cd92a1adba2a54150b39990e4716f05d165cebd..c3e5ac41cb8c783c74ee981e97be7f9407b89c27 100644
--- a/lib/banzai/filter/commit_reference_filter.rb
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces commit references with links.
diff --git a/lib/banzai/filter/commit_trailers_filter.rb b/lib/banzai/filter/commit_trailers_filter.rb
index 7b55e8b36f6d372e942bd4005387e2b1db797dcd..f49c4b403db432781dcc66583abed7cb1d075f44 100644
--- a/lib/banzai/filter/commit_trailers_filter.rb
+++ b/lib/banzai/filter/commit_trailers_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces users' names and emails in commit trailers
diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index 4eccd9d5ed54406a74278686e80b54a928326ea9..c87948a30bf9fc567dd0dd47bf92ff921338838d 100644
--- a/lib/banzai/filter/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces :emoji: and unicode with images.
diff --git a/lib/banzai/filter/epic_reference_filter.rb b/lib/banzai/filter/epic_reference_filter.rb
index 265924abe24529e359b49058867d7cd71dd57ed6..e06e2fb3870e621b274796b3dc9ef84c88112652 100644
--- a/lib/banzai/filter/epic_reference_filter.rb
+++ b/lib/banzai/filter/epic_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # The actual filter is implemented in the EE mixin
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index ed01a72ff9f7cbd87db6c636c3281746ec518187..b4a7a44e109522e014c6b975a4d264bcc5924957 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces external issue tracker references with links.
diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index d6327ef31cbc41f9d58c8b40fda9523c423925c7..2e6d742de277f0664a54bb3f6d3bef0ade7f6977 100644
--- a/lib/banzai/filter/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML Filter to modify the attributes of external links
diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb
index bb9f488cd87b970972cbd7794734db8486fc2e39..0c1bbd2d250a8b5ae71d40863902e80ce347ca32 100644
--- a/lib/banzai/filter/gollum_tags_filter.rb
+++ b/lib/banzai/filter/gollum_tags_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML Filter for parsing Gollum's tags in HTML. It's only parses the
diff --git a/lib/banzai/filter/html_entity_filter.rb b/lib/banzai/filter/html_entity_filter.rb
index e008fd428b06863da3a803eb624b6790586b4cad..406c2d3c96b627ac8584d728e3d217ebad417f45 100644
--- a/lib/banzai/filter/html_entity_filter.rb
+++ b/lib/banzai/filter/html_entity_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'erb'
 
 module Banzai
diff --git a/lib/banzai/filter/image_lazy_load_filter.rb b/lib/banzai/filter/image_lazy_load_filter.rb
index 4cd9b02b76c45d1f937ace15f47498761afe4b58..afaee70f351c8ffef4745d257ddc93a606d14092 100644
--- a/lib/banzai/filter/image_lazy_load_filter.rb
+++ b/lib/banzai/filter/image_lazy_load_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that moves the value of image `src` attributes to `data-src`
@@ -5,7 +7,7 @@ module Banzai
     class ImageLazyLoadFilter < HTML::Pipeline::Filter
       def call
         doc.xpath('descendant-or-self::img').each do |img|
-          img['class'] ||= '' << 'lazy'
+          img.add_class('lazy')
           img['data-src'] = img['src']
           img['src'] = LazyImageTagHelper.placeholder_image
         end
diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb
index f318c425962e060ea7811c3408f677f0516c2d28..884a94fb761dc51911c7b92bde1ac6fabd4cd0e4 100644
--- a/lib/banzai/filter/image_link_filter.rb
+++ b/lib/banzai/filter/image_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that wraps links around inline images.
diff --git a/lib/banzai/filter/inline_diff_filter.rb b/lib/banzai/filter/inline_diff_filter.rb
index 73e82a4d7e3a8f306fffca102101b852da9d3826..e9ddc6e0e3d4f4649c3dd54a183edb85ea0c8620 100644
--- a/lib/banzai/filter/inline_diff_filter.rb
+++ b/lib/banzai/filter/inline_diff_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     class InlineDiffFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/issuable_reference_filter.rb b/lib/banzai/filter/issuable_reference_filter.rb
index 7addf09be7328ee52249f5b7c2ae7f4716ee9c29..2963cba91e872f470ebaf07b031c703beaa4e310 100644
--- a/lib/banzai/filter/issuable_reference_filter.rb
+++ b/lib/banzai/filter/issuable_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     class IssuableReferenceFilter < AbstractReferenceFilter
diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb
index 1a4152325456f1d3718310ca7232efce6095a046..d7fe012883dbc16c05641bbd94486e28190d419f 100644
--- a/lib/banzai/filter/issuable_state_filter.rb
+++ b/lib/banzai/filter/issuable_state_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that appends state information to issuable links.
diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 6877cae8c55e234b4a855fae26741a4956ec4241..f85be0429994a11fdd8023dfa67530456d1817aa 100644
--- a/lib/banzai/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces issue references with links. References to
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index a5f38046a4330f615de534282272f3a92999bcc5..b92e9e55bb92c9eb4fdd54c489ea0b8fb3077260 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces label references with links.
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index 944363f17d341c190316fa2c932a97857ceb3805..cdf758472c13999f26433d94dd1bd31b1b40a988 100644
--- a/lib/banzai/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     class MarkdownFilter < HTML::Pipeline::TextFilter
diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb
index b6e784c886ba8a65dae6122090078aa0300c81fc..9d1bc3cf60ca31789ae4250f7286dd7d6bb478d8 100644
--- a/lib/banzai/filter/math_filter.rb
+++ b/lib/banzai/filter/math_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'uri'
 
 module Banzai
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 10c40568006c8b374a1571d38f1dbd71069098f3..7098767b58322b775bd44a0bbd039bf796a2243f 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces merge request references with links. References
diff --git a/lib/banzai/filter/mermaid_filter.rb b/lib/banzai/filter/mermaid_filter.rb
index 65c131e08d9cf4db44a841c90f4a2a209d627be5..7c8b165a330aef467a6b5c52fb19ba2566760761 100644
--- a/lib/banzai/filter/mermaid_filter.rb
+++ b/lib/banzai/filter/mermaid_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     class MermaidFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index af8448937b372fe55e6c83429c1e7449db5e0ab7..328c8c1803be8d48cfa6c643b62713ce4efd9de3 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces milestone references with links.
diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb
index 28933c789667292209ec93ef8d853275174cbfd1..caba8955bac106566efce98aa5c2d5a06c44364f 100644
--- a/lib/banzai/filter/plantuml_filter.rb
+++ b/lib/banzai/filter/plantuml_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "nokogiri"
 require "asciidoctor-plantuml/plantuml"
 
diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index caf11fe94c48feca411efa0b58a2936561033c36..1f091f594f86d62c2a4d6b3ac138cf7f59e198c7 100644
--- a/lib/banzai/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that removes references to records that the current user does
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 2411dd2cdfc60936483229fe07edadf42f96275f..e5164e7f72a94c034e74c31b7ea1ef411b3b4e9d 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # Base class for GitLab Flavored Markdown reference filters.
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 262458a872a56a73e76c28d742dcb7736e77b229..8e838d04badfe11c46ba6071723861849a62ae5d 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'uri'
 
 module Banzai
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index 8275bb9e14928d56e72a75707eee3f3b082fdc4e..8ba09290e6dd784378028bac01ad7e231b089d70 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # Sanitize HTML
@@ -11,7 +13,7 @@ module Banzai
 
       def whitelist
         strong_memoize(:whitelist) do
-          customize_whitelist(super.dup)
+          customize_whitelist(super.deep_dup)
         end
       end
 
diff --git a/lib/banzai/filter/set_direction_filter.rb b/lib/banzai/filter/set_direction_filter.rb
index c2976aeb7c6dff663fa95e389722b2ed2a7cdc4f..45b259a2faf1bb9b7610f2db75aeb64cd230d43e 100644
--- a/lib/banzai/filter/set_direction_filter.rb
+++ b/lib/banzai/filter/set_direction_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that sets dir="auto" for RTL languages support
diff --git a/lib/banzai/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb
index 881e10afb9f21e78e9ede4b2c891c8cd9e36e838..f4b6edb61742d2e6dbb016eb58808b2f40d68157 100644
--- a/lib/banzai/filter/snippet_reference_filter.rb
+++ b/lib/banzai/filter/snippet_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces snippet references with links. References to
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 6dbf0d68fe8f0d9c05a022ef610341bb81b99a53..8a7f9045c24f218f2d6e9d5344c6ff7c4379e8b9 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rouge/plugins/common_mark'
 require 'rouge/plugins/redcarpet'
 
@@ -15,7 +17,7 @@ module Banzai
       end
 
       def highlight_node(node)
-        css_classes = 'code highlight js-syntax-highlight'
+        css_classes = +'code highlight js-syntax-highlight'
         lang = node.attr('lang')
         retried = false
 
diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index b32660a83418c6a6cccbd95c2241ecb4a9f226b5..c6d1e028eaad52fe4b0b1ce0e9c1d6f695a59156 100644
--- a/lib/banzai/filter/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that adds an anchor child element to all Headers in a
@@ -19,7 +21,7 @@ module Banzai
       def call
         return doc if context[:no_header_anchors]
 
-        result[:toc] = ""
+        result[:toc] = +""
 
         headers = Hash.new(0)
         header_root = current_header = HeaderNode.new
diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb
index 9fa5f589f3ec258aed87bae0863a45b66a45908a..ef35a49edcba3a41d40c20a811fe6ff59634fe67 100644
--- a/lib/banzai/filter/task_list_filter.rb
+++ b/lib/banzai/filter/task_list_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'task_list/filter'
 
 module Banzai
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index c7fa8a8119f6d5f971ef9c705d0581738e7ce8d1..11960047e5baedcb9ae53dd86892c56ca494115a 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # HTML filter that replaces user or group references with links.
diff --git a/lib/banzai/filter/video_link_filter.rb b/lib/banzai/filter/video_link_filter.rb
index 35cb10eae5dd068bd00bd3141c2314bdaafe8905..0fb59c914c37c8b3a2bc3c7eaf7dd4ad078aa01d 100644
--- a/lib/banzai/filter/video_link_filter.rb
+++ b/lib/banzai/filter/video_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     # Find every image that isn't already wrapped in an `a` tag, and that has
diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb
index 269d5bf74fa973603cf498e1ed990b362a51e70e..870721f895df44159f448b0dcdf9f362b4335db8 100644
--- a/lib/banzai/filter/wiki_link_filter.rb
+++ b/lib/banzai/filter/wiki_link_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'uri'
 
 module Banzai
diff --git a/lib/banzai/filter/yaml_front_matter_filter.rb b/lib/banzai/filter/yaml_front_matter_filter.rb
index 58e3e81209eea75dd8eebc7d34ddc35aff8d59bc..295964dd75d4b009b591026dfbe1dde4fee66436 100644
--- a/lib/banzai/filter/yaml_front_matter_filter.rb
+++ b/lib/banzai/filter/yaml_front_matter_filter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Banzai
   module Filter
     class YamlFrontMatterFilter < HTML::Pipeline::Filter
diff --git a/lib/feature.rb b/lib/feature.rb
index 314ae224d9029ba153a28bee13815ea72d7fe72f..d27b2b0e72fd445ea1ef38a51cdcd6985bbd4641 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -39,7 +39,7 @@ class Feature
       # Flipper creates on-memory features when asked for a not-yet-created one.
       # If we want to check if a feature has been actually set, we look for it
       # on the persisted features list.
-      persisted_names.include?(feature.name)
+      persisted_names.include?(feature.name.to_s)
     end
 
     def enabled?(key, thing = nil)
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 7de66539848115f4c1384285c00bb89759085571..111e18b20763fa54ad0ed8f857a45eeb0734f664 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -14,23 +14,8 @@ module Gitlab
     DEFAULT_SCOPES = [:api].freeze
 
     class << self
-      def omniauth_customized_providers
-        @omniauth_customized_providers ||= %w[bitbucket jwt]
-      end
-
-      def omniauth_setup_providers(provider_names)
-        provider_names.each do |provider|
-          omniauth_setup_a_provider(provider)
-        end
-      end
-
-      def omniauth_setup_a_provider(provider)
-        case provider
-        when 'kerberos'
-          require 'omniauth-kerberos'
-        when *omniauth_customized_providers
-          require_dependency "omni_auth/strategies/#{provider}"
-        end
+      def omniauth_enabled?
+        Gitlab.config.omniauth.enabled
       end
 
       def find_for_git_client(login, password, project:, ip:)
diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb
index 5fb61ffe00d7be5b62d179c7b9d464e4ddc09304..e73743944a9d6354037f79734190096e05be4d36 100644
--- a/lib/gitlab/auth/o_auth/provider.rb
+++ b/lib/gitlab/auth/o_auth/provider.rb
@@ -30,7 +30,7 @@ module Gitlab
         def self.enabled?(name)
           return true if name == 'database'
 
-          providers.include?(name.to_sym)
+          Gitlab::Auth.omniauth_enabled? && providers.include?(name.to_sym)
         end
 
         def self.ldap_provider?(name)
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 872e70f9a5d1f2fbb00480f87a465729b1872073..8eacad078c8ac7c8d4e1d59c610c2dacef74a350 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -61,6 +61,10 @@ module Gitlab
       @version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
     end
 
+    def self.postgresql_9_or_less?
+      postgresql? && version.to_f < 10
+    end
+
     def self.join_lateral_supported?
       postgresql? && version.to_f >= 9.3
     end
@@ -69,6 +73,28 @@ module Gitlab
       postgresql? && version.to_f >= 9.4
     end
 
+    def self.pg_stat_wal_receiver_supported?
+      postgresql? && version.to_f >= 9.6
+    end
+
+    # map some of the function names that changed between PostgreSQL 9 and 10
+    # https://wiki.postgresql.org/wiki/New_in_postgres_10
+    def self.pg_wal_lsn_diff
+      Gitlab::Database.postgresql_9_or_less? ? 'pg_xlog_location_diff' : 'pg_wal_lsn_diff'
+    end
+
+    def self.pg_current_wal_insert_lsn
+      Gitlab::Database.postgresql_9_or_less? ? 'pg_current_xlog_insert_location' : 'pg_current_wal_insert_lsn'
+    end
+
+    def self.pg_last_wal_receive_lsn
+      Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_receive_location' : 'pg_last_wal_receive_lsn'
+    end
+
+    def self.pg_last_wal_replay_lsn
+      Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_replay_location' : 'pg_last_wal_replay_lsn'
+    end
+
     def self.nulls_last_order(field, direction = 'ASC')
       order = "#{field} #{direction}"
 
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 5b955753a9233e078e26dc63bd1d4312580101e0..0356e8efc5c4e73a39e4a0515ba873e6d3f28bcd 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -353,8 +353,6 @@ module Gitlab
       #     offset: 5,
       #     after: Time.new(2016, 4, 21, 14, 32, 10)
       #   )
-      #
-      # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/446
       def log(options)
         default_options = {
           limit: 10,
@@ -826,6 +824,10 @@ module Gitlab
         end
       end
 
+      # This method, fetch_ref, is used from within
+      # Gitlab::Git::OperationService. OperationService will eventually only
+      # exist in gitaly-ruby. When we delete OperationService from gitlab-ce
+      # we can also remove fetch_ref.
       def fetch_ref(source_repository, source_ref:, target_ref:)
         Gitlab::Git.check_namespace!(source_repository)
         source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository)
@@ -1025,8 +1027,8 @@ module Gitlab
       end
 
       def clean_stale_repository_files
-        gitaly_migrate(:repository_cleanup, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
-          gitaly_repository_client.cleanup if is_enabled && exists?
+        wrapped_gitaly_errors do
+          gitaly_repository_client.cleanup if exists?
         end
       rescue Gitlab::Git::CommandError => e # Don't fail if we can't cleanup
         Rails.logger.error("Unable to clean repository on storage #{storage} with relative path #{relative_path}: #{e.message}")
@@ -1253,14 +1255,6 @@ module Gitlab
         run_git(args, env: source_repository.fetch_env)
       end
 
-      def rugged_add_remote(remote_name, url, mirror_refmap)
-        rugged.remotes.create(remote_name, url)
-
-        set_remote_as_mirror(remote_name, refmap: mirror_refmap) if mirror_refmap
-      rescue Rugged::ConfigError
-        remote_update(remote_name, url: url)
-      end
-
       def gitaly_delete_refs(*ref_names)
         gitaly_ref_client.delete_refs(refs: ref_names) if ref_names.any?
       end
diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb
index 8ee46b5983084fccf8841300a3b922be6a059064..9d992be66eb33a480cbbeda9308902ea17c90e64 100644
--- a/lib/gitlab/git/wiki.rb
+++ b/lib/gitlab/git/wiki.rb
@@ -44,9 +44,9 @@ module Gitlab
         end
       end
 
-      def pages(limit: nil)
+      def pages(limit: 0)
         @repository.wrapped_gitaly_errors do
-          gitaly_get_all_pages
+          gitaly_get_all_pages(limit: limit)
         end
       end
 
@@ -158,8 +158,8 @@ module Gitlab
         Gitlab::Git::WikiFile.new(wiki_file)
       end
 
-      def gitaly_get_all_pages
-        gitaly_wiki_client.get_all_pages.map do |wiki_page, version|
+      def gitaly_get_all_pages(limit: 0)
+        gitaly_wiki_client.get_all_pages(limit: limit).map do |wiki_page, version|
           Gitlab::Git::WikiPage.new(wiki_page, version)
         end
       end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 58a4060cc96f04f0630f0f7a85d20c53cb11a488..c27972a84a431254beeba9b152b66d7a8b3138a6 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -407,7 +407,7 @@ module Gitlab
 
     # The default timeout on all Gitaly calls
     def self.default_timeout
-      return 0 if Sidekiq.server?
+      return no_timeout if Sidekiq.server?
 
       timeout(:gitaly_timeout_default)
     end
@@ -420,6 +420,10 @@ module Gitlab
       timeout(:gitaly_timeout_medium)
     end
 
+    def self.no_timeout
+      0
+    end
+
     def self.timeout(timeout_name)
       Gitlab::CurrentSettings.current_application_settings[timeout_name]
     end
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index 64b9af4d70c9355edd418e9f7958f8606cd4d2de..2956ed4b9118ebc1b31886d5a6e6407eb51894dd 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -202,7 +202,7 @@ module Gitlab
           save_path,
           :create_bundle,
           Gitaly::CreateBundleRequest,
-          GitalyClient.default_timeout
+          GitalyClient.no_timeout
         )
       end
 
@@ -220,7 +220,7 @@ module Gitlab
           bundle_path,
           :create_repository_from_bundle,
           Gitaly::CreateRepositoryFromBundleRequest,
-          GitalyClient.default_timeout
+          GitalyClient.no_timeout
         )
       end
 
@@ -245,7 +245,7 @@ module Gitlab
           :repository_service,
           :create_repository_from_snapshot,
           request,
-          timeout: GitalyClient.default_timeout
+          timeout: GitalyClient.no_timeout
         )
       end
 
diff --git a/lib/gitlab/gitaly_client/wiki_service.rb b/lib/gitlab/gitaly_client/wiki_service.rb
index 6cb049c1f68c2913e22aa8ce9978ecc40bc35e18..75be7d1f5a0b7560cf18851280f14b393ed12c0d 100644
--- a/lib/gitlab/gitaly_client/wiki_service.rb
+++ b/lib/gitlab/gitaly_client/wiki_service.rb
@@ -85,8 +85,8 @@ module Gitlab
         wiki_page_from_iterator(response)
       end
 
-      def get_all_pages
-        request = Gitaly::WikiGetAllPagesRequest.new(repository: @gitaly_repo)
+      def get_all_pages(limit: 0)
+        request = Gitaly::WikiGetAllPagesRequest.new(repository: @gitaly_repo, limit: limit)
         response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_all_pages, request, timeout: GitalyClient.medium_timeout)
         pages = []
 
diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb
index 22719e9a003902ae9e74cc1c9dc9dd8c145c411b..38ef12491df91551ad44ee42714740fd2a1641b5 100644
--- a/lib/gitlab/gitlab_import/client.rb
+++ b/lib/gitlab/gitlab_import/client.rb
@@ -32,15 +32,15 @@ module Gitlab
         api.get("/api/v4/user").parsed
       end
 
-      def issues(project_identifier)
-        lazy_page_iterator(PER_PAGE) do |page|
-          api.get("/api/v4/projects/#{project_identifier}/issues?per_page=#{PER_PAGE}&page=#{page}").parsed
+      def issues(project_identifier, **kwargs)
+        lazy_page_iterator(**kwargs) do |page, per_page|
+          api.get("/api/v4/projects/#{project_identifier}/issues?per_page=#{per_page}&page=#{page}").parsed
         end
       end
 
-      def issue_comments(project_identifier, issue_id)
-        lazy_page_iterator(PER_PAGE) do |page|
-          api.get("/api/v4/projects/#{project_identifier}/issues/#{issue_id}/notes?per_page=#{PER_PAGE}&page=#{page}").parsed
+      def issue_comments(project_identifier, issue_id, **kwargs)
+        lazy_page_iterator(**kwargs) do |page, per_page|
+          api.get("/api/v4/projects/#{project_identifier}/issues/#{issue_id}/notes?per_page=#{per_page}&page=#{page}").parsed
         end
       end
 
@@ -48,23 +48,27 @@ module Gitlab
         api.get("/api/v4/projects/#{id}").parsed
       end
 
-      def projects
-        lazy_page_iterator(PER_PAGE) do |page|
-          api.get("/api/v4/projects?per_page=#{PER_PAGE}&page=#{page}").parsed
+      def projects(**kwargs)
+        lazy_page_iterator(**kwargs) do |page, per_page|
+          api.get("/api/v4/projects?per_page=#{per_page}&page=#{page}&simple=true&membership=true").parsed
         end
       end
 
       private
 
-      def lazy_page_iterator(per_page)
+      def lazy_page_iterator(starting_page: 1, page_limit: nil, per_page: PER_PAGE)
         Enumerator.new do |y|
-          page = 1
+          page = starting_page
+          page_limit = (starting_page - 1) + page_limit if page_limit
+
           loop do
-            items = yield(page)
+            items = yield(page, per_page)
+
             items.each do |item|
               y << item
             end
-            break if items.empty? || items.size < per_page
+
+            break if items.empty? || items.size < per_page || (page_limit && page >= page_limit)
 
             page += 1
           end
diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb
index f3d7407383cdb1ef24eb59ddd9966e8d29641e22..d0527f014a7c4029da5d159560fccc155f6537c8 100644
--- a/lib/gitlab/import_export/merge_request_parser.rb
+++ b/lib/gitlab/import_export/merge_request_parser.rb
@@ -25,6 +25,7 @@ module Gitlab
         @project.repository.create_branch(@merge_request.target_branch, @merge_request.target_branch_sha)
       end
 
+      # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1295
       def fetch_ref
         @project.repository.fetch_ref(@project.repository, source_ref: @diff_head_sha, target_ref: @merge_request.source_branch)
       end
diff --git a/lib/gitlab/omniauth_initializer.rb b/lib/gitlab/omniauth_initializer.rb
index a71acda87016d61345d63c3c1e188eadcbe1c308..f33ea0880df8f927ccd8a38be7f78b082e476f51 100644
--- a/lib/gitlab/omniauth_initializer.rb
+++ b/lib/gitlab/omniauth_initializer.rb
@@ -1,23 +1,21 @@
 module Gitlab
   class OmniauthInitializer
-    def self.enabled?
-      Gitlab.config.omniauth.enabled ||
-        Gitlab.config.omniauth.auto_sign_in_with_provider.present?
-    end
-
     def initialize(devise_config)
       @devise_config = devise_config
     end
 
     def execute(providers)
       providers.each do |provider|
-        add_provider(provider['name'].to_sym, *arguments_for(provider))
+        name = provider['name'].to_sym
+
+        add_provider_to_devise(name, *arguments_for(provider))
+        setup_provider(name)
       end
     end
 
     private
 
-    def add_provider(*args)
+    def add_provider_to_devise(*args)
       @devise_config.omniauth(*args)
     end
 
@@ -76,5 +74,23 @@ module Gitlab
         end
       end
     end
+
+    def omniauth_customized_providers
+      @omniauth_customized_providers ||= build_omniauth_customized_providers
+    end
+
+    # We override this in EE
+    def build_omniauth_customized_providers
+      %i[bitbucket jwt]
+    end
+
+    def setup_provider(provider)
+      case provider
+      when :kerberos
+        require 'omniauth-kerberos'
+      when *omniauth_customized_providers
+        require_dependency "omni_auth/strategies/#{provider}"
+      end
+    end
   end
 end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index dff0c97eeb4a14cb7e50e33f4401a4a814fd27ab..22c9638ecc0cae043b853a3dca8769d96c5eb039 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -95,7 +95,7 @@ module Gitlab
           gravatar_enabled: Gitlab::CurrentSettings.gravatar_enabled?,
           ldap_enabled: Gitlab.config.ldap.enabled,
           mattermost_enabled: Gitlab.config.mattermost.enabled,
-          omniauth_enabled: Gitlab.config.omniauth.enabled,
+          omniauth_enabled: Gitlab::Auth.omniauth_enabled?,
           reply_by_email_enabled: Gitlab::IncomingEmail.enabled?,
           signup_enabled: Gitlab::CurrentSettings.allow_signup?
         }
diff --git a/lib/support/nginx/registry-ssl b/lib/support/nginx/registry-ssl
index 92511e268615f4c181339ecf802f8641b9310cee..908d26a0da244a71f18b4b7fb966be3eae1d0492 100644
--- a/lib/support/nginx/registry-ssl
+++ b/lib/support/nginx/registry-ssl
@@ -10,7 +10,7 @@ server {
   listen *:80;
   server_name  registry.gitlab.example.com;
   server_tokens off; ## Don't show the nginx version number, a security best practice
-  return 301 https://$http_host:$request_uri;
+  return 301 https://$http_host$request_uri;
   access_log  /var/log/nginx/gitlab_registry_access.log gitlab_access;
   error_log   /var/log/nginx/gitlab_registry_error.log;
 }
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index 6de739e95158eb444c4e2101cd4f83781078fb83..e97d77d20e0161e4e9074d74772767d3638fd9a3 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -54,8 +54,8 @@ namespace :gitlab do
       puts "HTTP Clone URL:\t#{http_clone_url}"
       puts "SSH Clone URL:\t#{ssh_clone_url}"
       puts "Using LDAP:\t#{Gitlab.config.ldap.enabled ? "yes".color(:green) : "no"}"
-      puts "Using Omniauth:\t#{Gitlab.config.omniauth.enabled ? "yes".color(:green) : "no"}"
-      puts "Omniauth Providers: #{omniauth_providers.join(', ')}" if Gitlab.config.omniauth.enabled
+      puts "Using Omniauth:\t#{Gitlab::Auth.omniauth_enabled? ? "yes".color(:green) : "no"}"
+      puts "Omniauth Providers: #{omniauth_providers.join(', ')}" if Gitlab::Auth.omniauth_enabled?
 
       # check Gitolite version
       gitlab_shell_version_file = "#{Gitlab.config.gitlab_shell.hooks_path}/../VERSION"
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 53eeb67672f9de8fad422d588b002c79c072a03a..a313713a9a828e63fa745f6955056e400e2498e8 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,6 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gitlab 1.0.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-07-10 16:02-0700\n"
-"PO-Revision-Date: 2018-07-10 16:02-0700\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "Language: \n"
@@ -240,6 +238,12 @@ msgstr ""
 msgid "A user with write access to the source branch selected this option"
 msgstr ""
 
+msgid "About GitLab"
+msgstr ""
+
+msgid "About GitLab CE"
+msgstr ""
+
 msgid "About auto deploy"
 msgstr ""
 
@@ -312,6 +316,15 @@ msgstr ""
 msgid "Add users to group"
 msgstr ""
 
+msgid "Admin Area"
+msgstr ""
+
+msgid "Admin Overview"
+msgstr ""
+
+msgid "Admin area"
+msgstr ""
+
 msgid "AdminArea|Stop all jobs"
 msgstr ""
 
@@ -465,7 +478,7 @@ msgstr ""
 msgid "An error occurred while importing project: ${details}"
 msgstr ""
 
-msgid "An error occurred while loading commits"
+msgid "An error occurred while loading commit signatures"
 msgstr ""
 
 msgid "An error occurred while loading diff"
@@ -504,6 +517,9 @@ msgstr ""
 msgid "Anonymous"
 msgstr ""
 
+msgid "Anti-spam verification"
+msgstr ""
+
 msgid "Any"
 msgstr ""
 
@@ -597,6 +613,9 @@ msgstr ""
 msgid "Authentication Log"
 msgstr ""
 
+msgid "Authentication log"
+msgstr ""
+
 msgid "Author"
 msgstr ""
 
@@ -621,6 +640,9 @@ msgstr ""
 msgid "Authors: %{authors}"
 msgstr ""
 
+msgid "Auto DevOps"
+msgstr ""
+
 msgid "Auto DevOps enabled"
 msgstr ""
 
@@ -678,6 +700,9 @@ msgstr ""
 msgid "Average per day: %{average}"
 msgstr ""
 
+msgid "Background Jobs"
+msgstr ""
+
 msgid "Background color"
 msgstr ""
 
@@ -768,6 +793,9 @@ msgstr ""
 msgid "Bitbucket import"
 msgstr ""
 
+msgid "Blog"
+msgstr ""
+
 msgid "Boards"
 msgstr ""
 
@@ -1532,6 +1560,9 @@ msgstr ""
 msgid "ClusterIntegration|sign up"
 msgstr ""
 
+msgid "Cohorts"
+msgstr ""
+
 msgid "Collapse"
 msgstr ""
 
@@ -1758,6 +1789,9 @@ msgstr ""
 msgid "Control the display of third party offers."
 msgstr ""
 
+msgid "ConvDev Index"
+msgstr ""
+
 msgid "Copy URL to clipboard"
 msgstr ""
 
@@ -1794,6 +1828,9 @@ msgstr ""
 msgid "Create a new branch and merge request"
 msgstr ""
 
+msgid "Create a new issue"
+msgstr ""
+
 msgid "Create a personal access token on your account to pull or push via %{protocol}."
 msgstr ""
 
@@ -1920,6 +1957,9 @@ msgstr ""
 msgid "CycleAnalyticsStage|Test"
 msgstr ""
 
+msgid "Dashboard"
+msgstr ""
+
 msgid "DashboardProjects|All"
 msgstr ""
 
@@ -2402,6 +2442,9 @@ msgstr ""
 msgid "Every week (Sundays at 4:00am)"
 msgstr ""
 
+msgid "Everyone can contribute"
+msgstr ""
+
 msgid "Expand"
 msgstr ""
 
@@ -2411,6 +2454,9 @@ msgstr ""
 msgid "Expand sidebar"
 msgstr ""
 
+msgid "Explore"
+msgstr ""
+
 msgid "Explore GitLab"
 msgstr ""
 
@@ -2426,6 +2472,9 @@ msgstr ""
 msgid "Explore public groups"
 msgstr ""
 
+msgid "Facebook"
+msgstr ""
+
 msgid "Failed"
 msgstr ""
 
@@ -2677,6 +2726,9 @@ msgstr ""
 msgid "Group avatar"
 msgstr ""
 
+msgid "Group details"
+msgstr ""
+
 msgid "Group info:"
 msgstr ""
 
@@ -3003,6 +3055,9 @@ msgstr ""
 msgid "Koding"
 msgstr ""
 
+msgid "Koding Dashboard"
+msgstr ""
+
 msgid "Kubernetes"
 msgstr ""
 
@@ -3125,6 +3180,9 @@ msgstr ""
 msgid "Leave the \"File type\" and \"Delivery method\" options on their default values."
 msgstr ""
 
+msgid "LinkedIn"
+msgstr ""
+
 msgid "List"
 msgstr ""
 
@@ -3164,9 +3222,15 @@ msgstr ""
 msgid "Locked to current projects"
 msgstr ""
 
+msgid "Logs"
+msgstr ""
+
 msgid "Make sure you're logged into the account that owns the projects you'd like to import."
 msgstr ""
 
+msgid "Manage Git repositories with fine-grained access controls that keep your code secure. Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki."
+msgstr ""
+
 msgid "Manage access"
 msgstr ""
 
@@ -3347,6 +3411,9 @@ msgstr ""
 msgid "Monitoring"
 msgstr ""
 
+msgid "More"
+msgstr ""
+
 msgid "More actions"
 msgstr ""
 
@@ -3389,6 +3456,9 @@ msgstr ""
 msgid "Nav|Sign out and sign in with a different account"
 msgstr ""
 
+msgid "Network"
+msgstr ""
+
 msgid "New"
 msgstr ""
 
@@ -3463,6 +3533,9 @@ msgstr ""
 msgid "New tag"
 msgstr ""
 
+msgid "New..."
+msgstr ""
+
 msgid "No"
 msgstr ""
 
@@ -3643,6 +3716,12 @@ msgstr ""
 msgid "Open in Xcode"
 msgstr ""
 
+msgid "Open sidebar"
+msgstr ""
+
+msgid "Open source software to collaborate on code"
+msgstr ""
+
 msgid "OpenedNDaysAgo|Opened"
 msgstr ""
 
@@ -3943,6 +4022,9 @@ msgstr ""
 msgid "Profile"
 msgstr ""
 
+msgid "Profile Settings"
+msgstr ""
+
 msgid "Profiles|Account scheduled for removal."
 msgstr ""
 
@@ -4273,6 +4355,9 @@ msgstr ""
 msgid "Remove project"
 msgstr ""
 
+msgid "Reply to this email directly or %{view_it_on_gitlab}."
+msgstr ""
+
 msgid "Repository"
 msgstr ""
 
@@ -4294,6 +4379,9 @@ msgstr ""
 msgid "Request Access"
 msgstr ""
 
+msgid "Requests Profiles"
+msgstr ""
+
 msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
 msgstr ""
 
@@ -4542,6 +4630,9 @@ msgstr ""
 msgid "Shared Runners"
 msgstr ""
 
+msgid "Sherlock Transactions"
+msgstr ""
+
 msgid "Show command"
 msgstr ""
 
@@ -4571,6 +4662,12 @@ msgstr[1] ""
 msgid "Side-by-side"
 msgstr ""
 
+msgid "Sign in"
+msgstr ""
+
+msgid "Sign in / Register"
+msgstr ""
+
 msgid "Sign out"
 msgstr ""
 
@@ -4784,6 +4881,9 @@ msgstr ""
 msgid "Status"
 msgstr ""
 
+msgid "Stop impersonation"
+msgstr ""
+
 msgid "Stop this environment"
 msgstr ""
 
@@ -4817,6 +4917,9 @@ msgstr ""
 msgid "System Hooks"
 msgstr ""
 
+msgid "System Info"
+msgstr ""
+
 msgid "Tag (%{tag_count})"
 msgid_plural "Tags (%{tag_count})"
 msgstr[0] ""
@@ -5056,6 +5159,9 @@ msgstr ""
 msgid "This directory"
 msgstr ""
 
+msgid "This group"
+msgstr ""
+
 msgid "This group does not provide any group Runners yet."
 msgstr ""
 
@@ -5345,12 +5451,18 @@ msgstr ""
 msgid "Todo"
 msgstr ""
 
+msgid "Todos"
+msgstr ""
+
 msgid "Toggle Sidebar"
 msgstr ""
 
 msgid "Toggle discussion"
 msgstr ""
 
+msgid "Toggle navigation"
+msgstr ""
+
 msgid "Toggle sidebar"
 msgstr ""
 
@@ -5387,7 +5499,7 @@ msgstr ""
 msgid "Try again"
 msgstr ""
 
-msgid "URL for Bitbucket Server"
+msgid "Twitter"
 msgstr ""
 
 msgid "Unable to load the diff. %{button_try_again}"
@@ -5479,6 +5591,9 @@ msgstr ""
 msgid "Use your global notification setting"
 msgstr ""
 
+msgid "User Settings"
+msgstr ""
+
 msgid "User and IP Rate Limits"
 msgstr ""
 
@@ -5512,6 +5627,9 @@ msgstr ""
 msgid "View group labels"
 msgstr ""
 
+msgid "View it on GitLab"
+msgstr ""
+
 msgid "View jobs"
 msgstr ""
 
@@ -5554,6 +5672,9 @@ msgstr ""
 msgid "Want to see the data? Please ask an administrator for access."
 msgstr ""
 
+msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
+msgstr ""
+
 msgid "We don't have enough data to show this stage."
 msgstr ""
 
@@ -5827,9 +5948,18 @@ msgstr ""
 msgid "You'll need to use different branch names to get a valid comparison."
 msgstr ""
 
+msgid "You're receiving this email because %{reason}."
+msgstr ""
+
+msgid "You're receiving this email because of your account on %{host}."
+msgstr ""
+
 msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}"
 msgstr ""
 
+msgid "YouTube"
+msgstr ""
+
 msgid "Your Groups"
 msgstr ""
 
@@ -5919,6 +6049,9 @@ msgstr ""
 msgid "here"
 msgstr ""
 
+msgid "https://your-bitbucket-server"
+msgstr ""
+
 msgid "import flow"
 msgstr ""
 
@@ -6145,6 +6278,9 @@ msgstr ""
 msgid "uses Kubernetes clusters to deploy your code!"
 msgstr ""
 
+msgid "view it on GitLab"
+msgstr ""
+
 msgid "with %{additions} additions, %{deletions} deletions."
 msgstr ""
 
diff --git a/qa/qa/page/menu/side.rb b/qa/qa/page/menu/side.rb
index c14a835c2c933b6b12e1aabce9358abb5dffd779..354ccec2a5a6c54cec7038ac81f60f0af69ddeed 100644
--- a/qa/qa/page/menu/side.rb
+++ b/qa/qa/page/menu/side.rb
@@ -5,8 +5,8 @@ module QA
         view 'app/views/layouts/nav/sidebar/_project.html.haml' do
           element :settings_item
           element :settings_link, 'link_to edit_project_path'
-          element :repository_link, "title: 'Repository'"
-          element :pipelines_settings_link, "title: 'CI / CD'"
+          element :repository_link, "title: _('Repository')"
+          element :pipelines_settings_link, "title: _('CI / CD')"
           element :operations_kubernetes_link, "title: _('Kubernetes')"
           element :issues_link, /link_to.*shortcuts-issues/
           element :issues_link_text, "Issues"
@@ -14,7 +14,7 @@ module QA
           element :merge_requests_link_text, "Merge Requests"
           element :top_level_items, '.sidebar-top-level-items'
           element :operations_section, "class: 'shortcuts-operations'"
-          element :activity_link, "title: 'Activity'"
+          element :activity_link, "title: _('Activity')"
           element :wiki_link_text, "Wiki"
           element :milestones_link
         end
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index 1d3dad4cda0f7ca1479a153f97b364c1aae6930a..c751b472535c1184e03f7bd3489c7690bd286b7b 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -14,7 +14,7 @@ module QA
 
         view 'app/views/layouts/header/_new_dropdown.haml' do
           element :new_menu_toggle
-          element :new_issue_link, "link_to 'New issue', new_project_issue_path(@project)"
+          element :new_issue_link, "link_to _('New issue'), new_project_issue_path(@project)"
         end
 
         view 'app/views/shared/_ref_switcher.html.haml' do
diff --git a/spec/config/object_store_settings_spec.rb b/spec/config/object_store_settings_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b1ada3c99db626320a9b5184b0ef6a14155573f6
--- /dev/null
+++ b/spec/config/object_store_settings_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+require Rails.root.join('config', 'object_store_settings.rb')
+
+describe ObjectStoreSettings do
+  describe '.parse' do
+    it 'should set correct default values' do
+      settings = described_class.parse(nil)
+
+      expect(settings['enabled']).to be false
+      expect(settings['direct_upload']).to be false
+      expect(settings['background_upload']).to be true
+      expect(settings['remote_directory']).to be nil
+    end
+
+    it 'respects original values' do
+      original_settings = Settingslogic.new({
+        'enabled' => true,
+        'remote_directory' => 'artifacts'
+      })
+
+      settings = described_class.parse(original_settings)
+
+      expect(settings['enabled']).to be true
+      expect(settings['direct_upload']).to be false
+      expect(settings['background_upload']).to be true
+      expect(settings['remote_directory']).to eq 'artifacts'
+    end
+  end
+end
diff --git a/spec/controllers/projects/wikis_controller_spec.rb b/spec/controllers/projects/wikis_controller_spec.rb
index 92addf30307ddd66d3718ccc0decb31d5fbbff0a..fed6677935e803d5b7ef9fe8ba62ce93b8d520df 100644
--- a/spec/controllers/projects/wikis_controller_spec.rb
+++ b/spec/controllers/projects/wikis_controller_spec.rb
@@ -1,8 +1,35 @@
 require 'spec_helper'
 
 describe Projects::WikisController do
-  let(:project) { create(:project_empty_repo, :public) }
+  let(:project) { create(:project, :public, :repository) }
   let(:user) { create(:user) }
+  let(:wiki) { ProjectWiki.new(project, user) }
+
+  describe 'GET #show' do
+    let(:wiki_title) { 'page-title-test' }
+
+    render_views
+
+    before do
+      create_page(wiki_title, 'hello world')
+    end
+
+    it 'limits the retrieved pages for the sidebar' do
+      sign_in(user)
+
+      expect(controller).to receive(:load_wiki).and_return(wiki)
+
+      # empty? call
+      expect(wiki).to receive(:pages).with(limit: 1).and_call_original
+      # Sidebar entries
+      expect(wiki).to receive(:pages).with(limit: 15).and_call_original
+
+      get :show, namespace_id: project.namespace, project_id: project, id: wiki_title
+
+      expect(response).to have_http_status(:ok)
+      expect(response.body).to include(wiki_title)
+    end
+  end
 
   describe 'POST #preview_markdown' do
     it 'renders json in a correct format' do
@@ -13,4 +40,12 @@ describe Projects::WikisController do
       expect(JSON.parse(response.body).keys).to match_array(%w(body references))
     end
   end
+
+  def create_page(name, content)
+    project.wiki.wiki.write_page(name, :markdown, content, commit_details(name))
+  end
+
+  def commit_details(name)
+    Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "created page #{name}")
+  end
 end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index b0acf4a49ac0f602c7e53fc2bc076967a9e389d2..071f96a729efa58057556dd099132c5f6abd5cde 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -2,6 +2,8 @@ require 'spec_helper'
 
 describe UsersController do
   let(:user) { create(:user) }
+  let(:private_user) { create(:user, private_profile: true) }
+  let(:public_user) { create(:user) }
 
   describe 'GET #show' do
     context 'with rendered views' do
@@ -98,16 +100,47 @@ describe UsersController do
 
         expect(assigns(:events)).to be_empty
       end
+
+      it 'hides events if the user has a private profile' do
+        Gitlab::DataBuilder::Push.build_sample(project, private_user)
+
+        get :show, username: private_user.username, format: :json
+
+        expect(assigns(:events)).to be_empty
+      end
     end
   end
 
   describe 'GET #calendar' do
-    it 'renders calendar' do
-      sign_in(user)
+    context 'for user' do
+      let(:project) { create(:project) }
+
+      before do
+        sign_in(user)
+        project.add_developer(user)
+      end
+
+      context 'with public profile' do
+        it 'renders calendar' do
+          push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user)
+          EventCreateService.new.push(project, public_user, push_data)
+
+          get :calendar, username: public_user.username, format: :json
 
-      get :calendar, username: user.username, format: :json
+          expect(response).to have_gitlab_http_status(200)
+        end
+      end
+
+      context 'with private profile' do
+        it 'does not render calendar' do
+          push_data = Gitlab::DataBuilder::Push.build_sample(project, private_user)
+          EventCreateService.new.push(project, private_user, push_data)
 
-      expect(response).to have_gitlab_http_status(200)
+          get :calendar, username: private_user.username, format: :json
+
+          expect(response).to have_gitlab_http_status(:not_found)
+        end
+      end
     end
 
     context 'forked project' do
@@ -150,9 +183,26 @@ describe UsersController do
       expect(assigns(:calendar_date)).to eq(Date.parse('2014-07-31'))
     end
 
-    it 'renders calendar_activities' do
-      get :calendar_activities, username: user.username
-      expect(response).to render_template('calendar_activities')
+    context 'for user' do
+      context 'with public profile' do
+        it 'renders calendar_activities' do
+          push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user)
+          EventCreateService.new.push(project, public_user, push_data)
+
+          get :calendar_activities, username: public_user.username
+          expect(assigns[:events]).not_to be_empty
+        end
+      end
+
+      context 'with private profile' do
+        it 'does not render calendar_activities' do
+          push_data = Gitlab::DataBuilder::Push.build_sample(project, private_user)
+          EventCreateService.new.push(project, private_user, push_data)
+
+          get :calendar_activities, username: private_user.username
+          expect(response).to have_gitlab_http_status(:not_found)
+        end
+      end
     end
   end
 
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index 845a7c5fc4207b87643825f4d900b30453efc51d..fd8629ae50404ae3252dc1c64a2f1d32794b8fb9 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -49,7 +49,7 @@ describe 'Multiple issue updating from issues#index', :js do
       click_update_issues_button
 
       page.within('.issue .controls') do
-        expect(find('.author_link')["title"]).to have_content(user.name)
+        expect(find('.author-link')["title"]).to have_content(user.name)
       end
     end
 
@@ -63,7 +63,7 @@ describe 'Multiple issue updating from issues#index', :js do
 
       click_link 'Unassigned'
       click_update_issues_button
-      expect(find('.issue:first-child .controls')).not_to have_css('.author_link')
+      expect(find('.issue:first-child .controls')).not_to have_css('.author-link')
     end
   end
 
diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb
index bb327159cb0f1bf7a58b5d1757fdc5d7446c2878..cb6603d3f50d938ca0f0b525e01c459692a3d81e 100644
--- a/spec/features/merge_requests/user_mass_updates_spec.rb
+++ b/spec/features/merge_requests/user_mass_updates_spec.rb
@@ -47,7 +47,7 @@ describe 'Merge requests > User mass updates', :js do
         change_assignee(user.name)
 
         page.within('.merge-request .controls') do
-          expect(find('.author_link')["title"]).to have_content(user.name)
+          expect(find('.author-link')["title"]).to have_content(user.name)
         end
       end
     end
@@ -62,7 +62,7 @@ describe 'Merge requests > User mass updates', :js do
       it 'removes assignee from the merge request' do
         change_assignee('Unassigned')
 
-        expect(find('.merge-request .controls')).not_to have_css('.author_link')
+        expect(find('.merge-request .controls')).not_to have_css('.author-link')
       end
     end
   end
diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb
index 23d8d606790573cdea4c2f497701ff938ae159a4..534cfe1eb127b6721882618e75f67f2b10ba5130 100644
--- a/spec/features/projects/commits/user_browses_commits_spec.rb
+++ b/spec/features/projects/commits/user_browses_commits_spec.rb
@@ -238,6 +238,5 @@ def check_author_link(email, author)
   author_link = find('.commit-author-link')
 
   expect(author_link['href']).to eq(user_path(author))
-  expect(author_link['title']).to eq(email)
   expect(find('.commit-author-name').text).to eq(author.name)
 end
diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb
index 3d05474dca2052b6b1707226d5b23aa014f4d76a..5003eb508c248854f412b93d8d12e505d2d80a94 100644
--- a/spec/features/signed_commits_spec.rb
+++ b/spec/features/signed_commits_spec.rb
@@ -7,7 +7,7 @@ describe 'GPG signed commits', :js do
     user = create :user, email: 'unrelated.user@example.org'
     project.add_maintainer(user)
 
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       create :gpg_key, key: GpgHelpers::User1.public_key, user: user
     end
 
@@ -21,7 +21,7 @@ describe 'GPG signed commits', :js do
     end
 
     # user changes his email which makes the gpg key verified
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       user.skip_reconfirmation!
       user.update!(email: GpgHelpers::User1.emails.first)
     end
@@ -48,7 +48,7 @@ describe 'GPG signed commits', :js do
     end
 
     # user adds the gpg key which makes the signature valid
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       create :gpg_key, key: GpgHelpers::User1.public_key, user: user
     end
 
@@ -66,7 +66,7 @@ describe 'GPG signed commits', :js do
     end
 
     let(:user_1_key) do
-      Sidekiq::Testing.inline! do
+      perform_enqueued_jobs do
         create :gpg_key, key: GpgHelpers::User1.public_key, user: user_1
       end
     end
@@ -79,7 +79,7 @@ describe 'GPG signed commits', :js do
     end
 
     let(:user_2_key) do
-      Sidekiq::Testing.inline! do
+      perform_enqueued_jobs do
         create :gpg_key, key: GpgHelpers::User2.public_key, user: user_2
       end
     end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index 3e2fb704bc6b3d6be257f6eb71a7c91114d59529..207c333c636e624f3edf1c467864f5d8cab49e80 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -3,15 +3,53 @@ require 'spec_helper'
 describe 'User page' do
   let(:user) { create(:user) }
 
-  it 'shows all the tabs' do
-    visit(user_path(user))
-
-    page.within '.nav-links' do
-      expect(page).to have_link('Activity')
-      expect(page).to have_link('Groups')
-      expect(page).to have_link('Contributed projects')
-      expect(page).to have_link('Personal projects')
-      expect(page).to have_link('Snippets')
+  context 'with public profile' do
+    it 'shows all the tabs' do
+      visit(user_path(user))
+
+      page.within '.nav-links' do
+        expect(page).to have_link('Activity')
+        expect(page).to have_link('Groups')
+        expect(page).to have_link('Contributed projects')
+        expect(page).to have_link('Personal projects')
+        expect(page).to have_link('Snippets')
+      end
+    end
+
+    it 'does not show private profile message' do
+      visit(user_path(user))
+
+      expect(page).not_to have_content("This user has a private profile")
+    end
+  end
+
+  context 'with private profile' do
+    let(:user) { create(:user, private_profile: true) }
+
+    it 'shows no tab' do
+      visit(user_path(user))
+
+      expect(page).to have_css("div.profile-header")
+      expect(page).not_to have_css("ul.nav-links")
+    end
+
+    it 'shows private profile message' do
+      visit(user_path(user))
+
+      expect(page).to have_content("This user has a private profile")
+    end
+
+    it 'shows own tabs' do
+      sign_in(user)
+      visit(user_path(user))
+
+      page.within '.nav-links' do
+        expect(page).to have_link('Activity')
+        expect(page).to have_link('Groups')
+        expect(page).to have_link('Contributed projects')
+        expect(page).to have_link('Personal projects')
+        expect(page).to have_link('Snippets')
+      end
     end
   end
 
diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb
index da043f940219c42494a4979930fe23cb83bf3562..58470f4c84d73802921e4bbc072d5b325421c24b 100644
--- a/spec/finders/user_recent_events_finder_spec.rb
+++ b/spec/finders/user_recent_events_finder_spec.rb
@@ -29,11 +29,22 @@ describe UserRecentEventsFinder do
         public_project.add_developer(current_user)
       end
 
-      it 'returns all the events' do
-        expect(finder.execute).to include(private_event, internal_event, public_event)
+      context 'when profile is public' do
+        it 'returns all the events' do
+          expect(finder.execute).to include(private_event, internal_event, public_event)
+        end
+      end
+
+      context 'when profile is private' do
+        it 'returns no event' do
+          allow(Ability).to receive(:allowed?).and_call_original
+          allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, project_owner).and_return(false)
+          expect(finder.execute).to be_empty
+        end
       end
 
       it 'does not include the events if the user cannot read cross project' do
+        expect(Ability).to receive(:allowed?).and_call_original
         expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false }
         expect(finder.execute).to be_empty
       end
diff --git a/spec/fixtures/api/schemas/pipeline.json b/spec/fixtures/api/schemas/pipeline.json
index 55511d17b5e6dbde955dcf510a17d16ecf601358..b6e30c40f1319b870638f52d9d0d54776265c746 100644
--- a/spec/fixtures/api/schemas/pipeline.json
+++ b/spec/fixtures/api/schemas/pipeline.json
@@ -319,6 +319,10 @@
             "id": "/properties/updated_at",
             "type": "string"
         },
+        "web_url": {
+            "id": "/properties/web_url",
+            "type": "string"
+        },
         "user": {
             "id": "/properties/user",
             "properties": {
diff --git a/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json b/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json
index 0d127dc52973993c1248fcefcedc215e529d2245..56f86856dd4215018ef1c6569911b7a92106b155 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pipeline/basic.json
@@ -4,13 +4,15 @@
     "id",
     "sha",
     "ref",
-    "status"
+    "status",
+    "web_url"
   ],
   "properties" : {
     "id": { "type": "integer" },
     "sha": { "type": "string" },
     "ref": { "type": "string" },
-    "status": { "type": "string" }
+    "status": { "type": "string" },
+    "web_url": { "type": "string" }
   },
   "additionalProperties": false
 }
diff --git a/spec/fixtures/api/schemas/public_api/v4/projects.json b/spec/fixtures/api/schemas/public_api/v4/projects.json
index 17ad8d8c48df08f1ecc3bb202efc87c017e4e9d4..af5670ebd334049aad9d95db211ac8926baf7da9 100644
--- a/spec/fixtures/api/schemas/public_api/v4/projects.json
+++ b/spec/fixtures/api/schemas/public_api/v4/projects.json
@@ -24,13 +24,24 @@
       "avatar_url": { "type": ["string", "null"] },
       "star_count": { "type": "integer" },
       "forks_count": { "type": "integer" },
-      "last_activity_at": { "type": "date" }
+      "last_activity_at": { "type": "date" },
+      "namespace": {
+        "type": "object",
+        "properties" : {
+          "id": { "type": "integer" },
+          "name": { "type": "string" },
+          "path": { "type": "string" },
+          "kind": { "type": "string" },
+          "full_path": { "type": "string" },
+          "parent_id": { "type": ["integer", "null"] }
+        }
+      }
     },
     "required": [
       "id", "name", "name_with_namespace", "description", "path",
       "path_with_namespace", "created_at", "default_branch", "tag_list",
       "ssh_url_to_repo", "http_url_to_repo", "web_url", "avatar_url",
-      "star_count", "last_activity_at"
+      "star_count", "last_activity_at", "namespace"
     ],
     "additionalProperties": false
   }
diff --git a/spec/helpers/button_helper_spec.rb b/spec/helpers/button_helper_spec.rb
index fee8df1012938b41be5f41cdf5db287f6faec1c0..630f3eff2584b43dd4ef10347be5fef3d3dba792 100644
--- a/spec/helpers/button_helper_spec.rb
+++ b/spec/helpers/button_helper_spec.rb
@@ -121,6 +121,8 @@ describe ButtonHelper do
   end
 
   describe 'clipboard_button' do
+    include IconsHelper
+
     let(:user) { create(:user) }
     let(:project) { build_stubbed(:project) }
 
@@ -145,7 +147,7 @@ describe ButtonHelper do
           expect(element.attr('data-clipboard-text')).to eq(nil)
           expect(element.inner_text).to eq("")
 
-          expect(element).to have_selector('.fa.fa-clipboard')
+          expect(element.to_html).to include sprite_icon('duplicate')
         end
       end
 
@@ -178,7 +180,7 @@ describe ButtonHelper do
 
     context 'with `hide_button_icon` attribute provided' do
       it 'shows copy to clipboard button without tooltip support' do
-        expect(element(hide_button_icon: true)).not_to have_selector('.fa.fa-clipboard')
+        expect(element(hide_button_icon: true).to_html).not_to include sprite_icon('duplicate')
       end
     end
   end
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index b18c045848f568be95c7fe682ef68313a7790bb3..b079802cb8197f2511ce6d04112f1725ea14989a 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -25,8 +25,20 @@ describe UsersHelper do
       allow(helper).to receive(:can?).and_return(true)
     end
 
-    it 'includes all the expected tabs' do
-      expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets)
+    context 'with public profile' do
+      it 'includes all the expected tabs' do
+        expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets)
+      end
+    end
+
+    context 'with private profile' do
+      before do
+        allow(helper).to receive(:can?).with(user, :read_user_profile, nil).and_return(false)
+      end
+
+      it 'is empty' do
+        expect(tabs).to be_empty
+      end
     end
   end
 
diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb
index 5077c89d7b45eb52662de726e44928740f007888..a3be222b7bdd32180a5f091b24eaf0df8ccb1614 100644
--- a/spec/helpers/visibility_level_helper_spec.rb
+++ b/spec/helpers/visibility_level_helper_spec.rb
@@ -6,6 +6,29 @@ describe VisibilityLevelHelper do
   let(:personal_snippet) { build(:personal_snippet) }
   let(:project_snippet)  { build(:project_snippet) }
 
+  describe 'visibility_icon_description' do
+    context 'used with a Project' do
+      it 'delegates projects to #project_visibility_icon_description' do
+        expect(visibility_icon_description(project))
+          .to match /project/i
+      end
+
+      context 'used with a ProjectPresenter' do
+        it 'delegates projects to #project_visibility_icon_description' do
+          expect(visibility_icon_description(project.present))
+            .to match /project/i
+        end
+      end
+
+      context 'used with a Group' do
+        it 'delegates groups to #group_visibility_icon_description' do
+          expect(visibility_icon_description(group))
+            .to match /group/i
+        end
+      end
+    end
+  end
+
   describe 'visibility_level_description' do
     context 'used with a Project' do
       it 'delegates projects to #project_visibility_level_description' do
diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
index cb85d12daf253f5b2feba39cdd422b9bf89bd437..bdc94131fc2d66689948589cc6fd05f98886c4cd 100644
--- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js
@@ -50,7 +50,11 @@ describe('DiffLineGutterContent', () => {
 
       it('should return discussions for the given lineCode', () => {
         const { lineCode } = getDiffFileMock().highlightedDiffLines[1];
-        const component = createComponent({ lineCode, showCommentButton: true });
+        const component = createComponent({
+          lineCode,
+          showCommentButton: true,
+          discussions: getDiscussionsMockData(),
+        });
 
         setDiscussions(component);
 
diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js
index 97c771dcfd358e93d5d8f8df05a28262f3025b35..78330dd9633deaade097bced27c651b6ee251ae7 100644
--- a/spec/javascripts/gpg_badges_spec.js
+++ b/spec/javascripts/gpg_badges_spec.js
@@ -1,23 +1,27 @@
 import MockAdapter from 'axios-mock-adapter';
 import axios from '~/lib/utils/axios_utils';
 import GpgBadges from '~/gpg_badges';
+import { TEST_HOST } from 'spec/test_constants';
 
 describe('GpgBadges', () => {
   let mock;
   const dummyCommitSha = 'n0m0rec0ffee';
   const dummyBadgeHtml = 'dummy html';
   const dummyResponse = {
-    signatures: [{
-      commit_sha: dummyCommitSha,
-      html: dummyBadgeHtml,
-    }],
+    signatures: [
+      {
+        commit_sha: dummyCommitSha,
+        html: dummyBadgeHtml,
+      },
+    ],
   };
+  const dummyUrl = `${TEST_HOST}/dummy/signatures`;
 
   beforeEach(() => {
     mock = new MockAdapter(axios);
     setFixtures(`
       <form
-        class="commits-search-form js-signature-container" data-signatures-path="/hello" action="/hello"
+        class="commits-search-form js-signature-container" data-signatures-path="${dummyUrl}" action="${dummyUrl}"
         method="get">
         <input name="utf8" type="hidden" value="✓">
         <input type="search" name="search" id="commits-search"class="form-control search-text-input input-short">
@@ -32,25 +36,55 @@ describe('GpgBadges', () => {
     mock.restore();
   });
 
-  it('displays a loading spinner', (done) => {
-    mock.onGet('/hello').reply(200);
+  it('does not make a request if there is no container element', done => {
+    setFixtures('');
+    spyOn(axios, 'get');
 
-    GpgBadges.fetch().then(() => {
-      expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
-      const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
-      expect(spinners.length).toBe(1);
-      done();
-    }).catch(done.fail);
+    GpgBadges.fetch()
+      .then(() => {
+        expect(axios.get).not.toHaveBeenCalled();
+      })
+      .then(done)
+      .catch(done.fail);
   });
 
-  it('replaces the loading spinner', (done) => {
-    mock.onGet('/hello').reply(200, dummyResponse);
+  it('throws an error if the endpoint is missing', done => {
+    setFixtures('<div class="js-signature-container"></div>');
+    spyOn(axios, 'get');
 
-    GpgBadges.fetch().then(() => {
-      expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
-      const parentContainer = document.querySelector('.parent-container');
-      expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
-      done();
-    }).catch(done.fail);
+    GpgBadges.fetch()
+      .then(() => done.fail('Expected error to be thrown'))
+      .catch(error => {
+        expect(error.message).toBe('Missing commit signatures endpoint!');
+        expect(axios.get).not.toHaveBeenCalled();
+      })
+      .then(done)
+      .catch(done.fail);
+  });
+
+  it('displays a loading spinner', done => {
+    mock.onGet(dummyUrl).replyOnce(200);
+
+    GpgBadges.fetch()
+      .then(() => {
+        expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
+        const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
+        expect(spinners.length).toBe(1);
+        done();
+      })
+      .catch(done.fail);
+  });
+
+  it('replaces the loading spinner', done => {
+    mock.onGet(dummyUrl).replyOnce(200, dummyResponse);
+
+    GpgBadges.fetch()
+      .then(() => {
+        expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
+        const parentContainer = document.querySelector('.parent-container');
+        expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
+        done();
+      })
+      .catch(done.fail);
   });
 });
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index eb5e0bddb74e7246aea8f3daee5cfe55112d4703..363283824485e9378a89f886026357ba9f0d4648 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -76,7 +76,7 @@ describe('Issuable output', () => {
       expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>this is a description!</p>');
       expect(vm.$el.querySelector('.js-task-list-field').value).toContain('this is a description');
       expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/);
-      expect(editedText.querySelector('.author_link').href).toMatch(/\/some_user$/);
+      expect(editedText.querySelector('.author-link').href).toMatch(/\/some_user$/);
       expect(editedText.querySelector('time')).toBeTruthy();
     })
     .then(() => {
@@ -90,7 +90,7 @@ describe('Issuable output', () => {
       expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42');
       expect(vm.$el.querySelector('.edited-text')).toBeTruthy();
       expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(/Edited[\s\S]+?by Other User/);
-      expect(editedText.querySelector('.author_link').href).toMatch(/\/other_user$/);
+      expect(editedText.querySelector('.author-link').href).toMatch(/\/other_user$/);
       expect(editedText.querySelector('time')).toBeTruthy();
     })
     .then(done)
diff --git a/spec/javascripts/issue_show/components/edited_spec.js b/spec/javascripts/issue_show/components/edited_spec.js
index 2061def699b45c8ead378eda2dde8484e99eda8d..7f09db837bb238702ffe03e6a269795b9ba2b1be 100644
--- a/spec/javascripts/issue_show/components/edited_spec.js
+++ b/spec/javascripts/issue_show/components/edited_spec.js
@@ -18,7 +18,7 @@ describe('edited', () => {
     }).$mount();
 
     expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited[\s\S]+?by Some User/);
-    expect(editedComponent.$el.querySelector('.author_link').href).toMatch(/\/some_user$/);
+    expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/);
     expect(editedComponent.$el.querySelector('time')).toBeTruthy();
   });
 
@@ -31,7 +31,7 @@ describe('edited', () => {
     }).$mount();
 
     expect(formatText(editedComponent.$el.innerText)).toMatch(/Edited by Some User/);
-    expect(editedComponent.$el.querySelector('.author_link').href).toMatch(/\/some_user$/);
+    expect(editedComponent.$el.querySelector('.author-link').href).toMatch(/\/some_user$/);
     expect(editedComponent.$el.querySelector('time')).toBeFalsy();
   });
 
@@ -43,7 +43,7 @@ describe('edited', () => {
     }).$mount();
 
     expect(formatText(editedComponent.$el.innerText)).not.toMatch(/by Some User/);
-    expect(editedComponent.$el.querySelector('.author_link')).toBeFalsy();
+    expect(editedComponent.$el.querySelector('.author-link')).toBeFalsy();
     expect(editedComponent.$el.querySelector('time')).toBeTruthy();
   });
 
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 41ff59949e56a33896c94cf836c12c465b8cfd0c..71b26a315af065799f6db9da910e716077c38752 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -627,4 +627,23 @@ describe('common_utils', () => {
       });
     });
   });
+
+  describe('roundOffFloat', () => {
+    it('Rounds off decimal places of a float number with provided precision', () => {
+      expect(commonUtils.roundOffFloat(3.141592, 3)).toBe(3.142);
+    });
+
+    it('Rounds off a float number to a whole number when provided precision is zero', () => {
+      expect(commonUtils.roundOffFloat(3.141592, 0)).toBe(3);
+      expect(commonUtils.roundOffFloat(3.5, 0)).toBe(4);
+    });
+
+    it('Rounds off float number to nearest 0, 10, 100, 1000 and so on when provided precision is below 0', () => {
+      expect(commonUtils.roundOffFloat(34567.14159, -1)).toBe(34570);
+      expect(commonUtils.roundOffFloat(34567.14159, -2)).toBe(34600);
+      expect(commonUtils.roundOffFloat(34567.14159, -3)).toBe(35000);
+      expect(commonUtils.roundOffFloat(34567.14159, -4)).toBe(30000);
+      expect(commonUtils.roundOffFloat(34567.14159, -5)).toBe(0);
+    });
+  });
 });
diff --git a/spec/javascripts/reports/store/actions_spec.js b/spec/javascripts/reports/store/actions_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..c714c5af156ee3a3ec7da00c67df545d8d0d3f55
--- /dev/null
+++ b/spec/javascripts/reports/store/actions_spec.js
@@ -0,0 +1,130 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import {
+  setEndpoint,
+  requestReports,
+  fetchReports,
+  stopPolling,
+  clearEtagPoll,
+  receiveReportsSuccess,
+  receiveReportsError,
+} from '~/reports/store/actions';
+import state from '~/reports/store/state';
+import * as types from '~/reports/store/mutation_types';
+import testAction from 'spec/helpers/vuex_action_helper';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('Reports Store Actions', () => {
+  let mockedState;
+
+  beforeEach(() => {
+    mockedState = state();
+  });
+
+  describe('setEndpoint', () => {
+    it('should commit SET_ENDPOINT mutation', done => {
+      testAction(
+        setEndpoint,
+        'endpoint.json',
+        mockedState,
+        [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }],
+        [],
+        done,
+      );
+    });
+  });
+
+  describe('requestReports', () => {
+    it('should commit REQUEST_REPORTS mutation', done => {
+      testAction(requestReports, null, mockedState, [{ type: types.REQUEST_REPORTS }], [], done);
+    });
+  });
+
+  describe('fetchReports', () => {
+    let mock;
+
+    beforeEach(() => {
+      mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
+      mock = new MockAdapter(axios);
+    });
+
+    afterEach(() => {
+      mock.restore();
+      stopPolling();
+      clearEtagPoll();
+    });
+
+    describe('success', () => {
+      it('dispatches requestReports and receiveReportsSuccess ', done => {
+        mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { summary: {}, suites: [{ name: 'rspec' }] });
+
+        testAction(
+          fetchReports,
+          null,
+          mockedState,
+          [],
+          [
+            {
+              type: 'requestReports',
+            },
+            {
+              payload: { summary: {}, suites: [{ name: 'rspec' }] },
+              type: 'receiveReportsSuccess',
+            },
+          ],
+          done,
+        );
+      });
+    });
+
+    describe('error', () => {
+      beforeEach(() => {
+        mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+      });
+
+      it('dispatches requestReports and receiveReportsError ', done => {
+        testAction(
+          fetchReports,
+          null,
+          mockedState,
+          [],
+          [
+            {
+              type: 'requestReports',
+            },
+            {
+              type: 'receiveReportsError',
+            },
+          ],
+          done,
+        );
+      });
+    });
+  });
+
+  describe('receiveReportsSuccess', () => {
+    it('should commit RECEIVE_REPORTS_SUCCESS mutation', done => {
+      testAction(
+        receiveReportsSuccess,
+        { summary: {} },
+        mockedState,
+        [{ type: types.RECEIVE_REPORTS_SUCCESS, payload: { summary: {} } }],
+        [],
+        done,
+      );
+    });
+  });
+
+  describe('receiveReportsError', () => {
+    it('should commit RECEIVE_REPORTS_ERROR mutation', done => {
+      testAction(
+        receiveReportsError,
+        null,
+        mockedState,
+        [{ type: types.RECEIVE_REPORTS_ERROR }],
+        [],
+        done,
+      );
+    });
+  });
+});
diff --git a/spec/javascripts/reports/store/mutations_spec.js b/spec/javascripts/reports/store/mutations_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..3e0b15438c3291e81b7679a62b107e641381eb7e
--- /dev/null
+++ b/spec/javascripts/reports/store/mutations_spec.js
@@ -0,0 +1,101 @@
+import state from '~/reports/store/state';
+import mutations from '~/reports/store/mutations';
+import * as types from '~/reports/store/mutation_types';
+
+describe('Reports Store Mutations', () => {
+  let stateCopy;
+
+  beforeEach(() => {
+    stateCopy = state();
+  });
+
+  describe('SET_ENDPOINT', () => {
+    it('should set endpoint', () => {
+      mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json');
+      expect(stateCopy.endpoint).toEqual('endpoint.json');
+    });
+  });
+
+  describe('REQUEST_REPORTS', () => {
+    it('should set isLoading to true', () => {
+      mutations[types.REQUEST_REPORTS](stateCopy);
+      expect(stateCopy.isLoading).toEqual(true);
+    });
+  });
+
+  describe('RECEIVE_REPORTS_SUCCESS', () => {
+    const mockedResponse = {
+      summary: {
+        total: 14,
+        resolved: 0,
+        failed: 7,
+      },
+      suites: [
+        {
+          name: 'build:linux',
+          summary: {
+            total: 2,
+            resolved: 0,
+            failed: 1,
+          },
+          new_failures: [
+            {
+              name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+              execution_time: 0.0092435,
+              system_output:
+                'Failure/Error: is_expected.to eq(\'gitlab\')',
+            },
+          ],
+          resolved_failures: [
+            {
+              name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+              execution_time: 0.009235,
+              system_output:
+                'Failure/Error: is_expected.to eq(\'gitlab\')',
+            },
+          ],
+          existing_failures: [
+            {
+              name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+              execution_time: 1232.08,
+              system_output:
+                'Failure/Error: is_expected.to eq(\'gitlab\')',
+            },
+          ],
+        },
+      ],
+    };
+
+    beforeEach(() => {
+      mutations[types.RECEIVE_REPORTS_SUCCESS](stateCopy, mockedResponse);
+    });
+
+    it('should reset isLoading', () => {
+      expect(stateCopy.isLoading).toEqual(false);
+    });
+
+    it('should set summary counts', () => {
+      expect(stateCopy.summary.total).toEqual(mockedResponse.summary.total);
+      expect(stateCopy.summary.resolved).toEqual(mockedResponse.summary.resolved);
+      expect(stateCopy.summary.failed).toEqual(mockedResponse.summary.failed);
+    });
+
+    it('should set reports', () => {
+      expect(stateCopy.reports).toEqual(mockedResponse.suites);
+    });
+  });
+
+  describe('RECEIVE_REPORTS_ERROR', () => {
+    beforeEach(() => {
+      mutations[types.RECEIVE_REPORTS_ERROR](stateCopy);
+    });
+    it('should reset isLoading', () => {
+      expect(stateCopy.isLoading).toEqual(false);
+    });
+
+    it('should set hasError to true', () => {
+      expect(stateCopy.hasError).toEqual(true);
+    });
+
+  });
+});
diff --git a/spec/javascripts/sidebar/assignees_spec.js b/spec/javascripts/sidebar/assignees_spec.js
index 4e4343812bd44fd04b50229b9943b35a2b75ef32..843e70021801c8119788b2af6b8a1831f104063d 100644
--- a/spec/javascripts/sidebar/assignees_spec.js
+++ b/spec/javascripts/sidebar/assignees_spec.js
@@ -102,13 +102,13 @@ describe('Assignee component', () => {
         },
       }).$mount();
 
-      expect(component.$el.querySelector('.author_link')).not.toBeNull();
+      expect(component.$el.querySelector('.author-link')).not.toBeNull();
       // The image
-      expect(component.$el.querySelector('.author_link img').getAttribute('src')).toEqual(UsersMock.user.avatar);
+      expect(component.$el.querySelector('.author-link img').getAttribute('src')).toEqual(UsersMock.user.avatar);
       // Author name
-      expect(component.$el.querySelector('.author_link .author').innerText.trim()).toEqual(UsersMock.user.name);
+      expect(component.$el.querySelector('.author-link .author').innerText.trim()).toEqual(UsersMock.user.name);
       // Username
-      expect(component.$el.querySelector('.author_link .username').innerText.trim()).toEqual(`@${UsersMock.user.username}`);
+      expect(component.$el.querySelector('.author-link .username').innerText.trim()).toEqual(`@${UsersMock.user.username}`);
     });
 
     it('has the root url present in the assigneeUrl method', () => {
diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
index 97f0fbb04dbec76e8243a91215274f442910bbf3..e135690349e8d833e3942af961043ea209929599 100644
--- a/spec/javascripts/vue_shared/components/clipboard_button_spec.js
+++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
@@ -21,7 +21,7 @@ describe('clipboard button', () => {
   it('renders a button for clipboard', () => {
     expect(vm.$el.tagName).toEqual('BUTTON');
     expect(vm.$el.getAttribute('data-clipboard-text')).toEqual('copy me');
-    expect(vm.$el.querySelector('i').className).toEqual('fa fa-clipboard');
+    expect(vm.$el).toHaveSpriteIcon('duplicate');
   });
 
   it('should have a tooltip with default values', () => {
diff --git a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
index de3bf667fb3b5b240ad545be060b865bfcd804fe..076d940961d75c5f4a59910626f59841aee6c46d 100644
--- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
+++ b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
@@ -10,9 +10,9 @@ const createComponent = (config) => {
     successLabel: 'Synced',
     failureLabel: 'Failed',
     neutralLabel: 'Out of sync',
-    successCount: 10,
-    failureCount: 5,
-    totalCount: 20,
+    successCount: 25,
+    failureCount: 10,
+    totalCount: 5000,
   }, config);
 
   return mountComponent(Component, defaultConfig);
@@ -32,7 +32,7 @@ describe('StackedProgressBarComponent', () => {
   describe('computed', () => {
     describe('neutralCount', () => {
       it('returns neutralCount based on totalCount, successCount and failureCount', () => {
-        expect(vm.neutralCount).toBe(5); // 20 - 10 - 5
+        expect(vm.neutralCount).toBe(4965); // 5000 - 25 - 10
       });
     });
   });
@@ -40,7 +40,11 @@ describe('StackedProgressBarComponent', () => {
   describe('methods', () => {
     describe('getPercent', () => {
       it('returns percentage from provided count based on `totalCount`', () => {
-        expect(vm.getPercent(10)).toBe(50);
+        expect(vm.getPercent(500)).toBe(10);
+      });
+
+      it('returns percentage with decimal place from provided count based on `totalCount`', () => {
+        expect(vm.getPercent(10)).toBe(0.2);
       });
     });
 
diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
index 41f957c4e0010042195ce69d3059b9a61f72458e..d06c5535309005085ea96aca2599ab43e7aaa75c 100644
--- a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
+++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
@@ -7,6 +7,20 @@ describe Banzai::Filter::ImageLazyLoadFilter do
     %(<img src="#{path}" />)
   end
 
+  def image_with_class(path, class_attr = nil)
+    %(<img src="#{path}" class="#{class_attr}"/>)
+  end
+
+  it 'adds a class attribute' do
+    doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
+    expect(doc.at_css('img')['class']).to eq 'lazy'
+  end
+
+  it 'appends to the current class attribute' do
+    doc = filter(image_with_class('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg', 'test'))
+    expect(doc.at_css('img')['class']).to eq 'test lazy'
+  end
+
   it 'transforms the image src to a data-src' do
     doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
     expect(doc.at_css('img')['data-src']).to eq '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'
diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index d930c608b1860b66a9c7c672365bd722867fe26c..0b3c23903049bd3277747935c60b2918942a461e 100644
--- a/spec/lib/banzai/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -54,6 +54,18 @@ describe Banzai::Filter::SanitizationFilter do
       expect(instance.whitelist[:transformers].size).to eq control_count
     end
 
+    it 'customizes the whitelist only once for different instances' do
+      instance1 = described_class.new('Foo1')
+      instance2 = described_class.new('Foo2')
+      control_count = instance1.whitelist[:transformers].size
+
+      instance1.whitelist
+      instance2.whitelist
+
+      expect(instance1.whitelist[:transformers].size).to eq control_count
+      expect(instance2.whitelist[:transformers].size).to eq control_count
+    end
+
     it 'sanitizes `class` attribute from all elements' do
       act = %q{<pre class="code highlight white c"><code>&lt;span class="k"&gt;def&lt;/span&gt;</code></pre>}
       exp = %q{<pre><code>&lt;span class="k"&gt;def&lt;/span&gt;</code></pre>}
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 6eb10497428cceecd2861cb90c5dc43ee91c2a55..f313e67565414a29c1db084efdfe02570feed950 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -39,18 +39,36 @@ describe Feature do
   end
 
   describe '.persisted?' do
-    it 'returns true for a persisted feature' do
-      Feature::FlipperFeature.create!(key: 'foo')
+    context 'when the feature is persisted' do
+      it 'returns true when feature name is a string' do
+        Feature::FlipperFeature.create!(key: 'foo')
+
+        feature = double(:feature, name: 'foo')
+
+        expect(described_class.persisted?(feature)).to eq(true)
+      end
+
+      it 'returns true when feature name is a symbol' do
+        Feature::FlipperFeature.create!(key: 'foo')
 
-      feature = double(:feature, name: 'foo')
+        feature = double(:feature, name: :foo)
 
-      expect(described_class.persisted?(feature)).to eq(true)
+        expect(described_class.persisted?(feature)).to eq(true)
+      end
     end
 
-    it 'returns false for a feature that is not persisted' do
-      feature = double(:feature, name: 'foo')
+    context 'when the feature is not persisted' do
+      it 'returns false when feature name is a string' do
+        feature = double(:feature, name: 'foo')
+
+        expect(described_class.persisted?(feature)).to eq(false)
+      end
 
-      expect(described_class.persisted?(feature)).to eq(false)
+      it 'returns false when feature name is a symbol' do
+        feature = double(:feature, name: :bar)
+
+        expect(described_class.persisted?(feature)).to eq(false)
+      end
     end
   end
 
diff --git a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
index 64c994a268fc7991e06c8668bbcd7d1a7e68b173..1969aed51da2f9b817abe8ccc5b9478f6d4151fe 100644
--- a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
+++ b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180619121030 do
+describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, :sidekiq, schema: 20180619121030 do
   describe '#perform' do
     context 'when diff files can be deleted' do
       let(:merge_request) { create(:merge_request, :merged) }
diff --git a/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb
index fb5093b0bd178667c73a8df3b71aa0fbd10c2697..ec8ba0ce1274ad1ec5560431782742f2cb7879c5 100644
--- a/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb
+++ b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::BackgroundMigration::ScheduleDiffFilesDeletion, :migration, schema: 20180619121030 do
+describe Gitlab::BackgroundMigration::ScheduleDiffFilesDeletion, :migration, :sidekiq, schema: 20180619121030 do
   describe '#perform' do
     let(:merge_request_diffs) { table(:merge_request_diffs) }
     let(:merge_requests) { table(:merge_requests) }
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 782e4e45a918662ae37056d0cec2dfc0aff03831..7d76519dddd6f44781edd18f8a48b3f51d9c43f7 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -65,6 +65,28 @@ describe Gitlab::Database do
     end
   end
 
+  describe '.postgresql_9_or_less?' do
+    it 'returns false when using MySQL' do
+      allow(described_class).to receive(:postgresql?).and_return(false)
+
+      expect(described_class.postgresql_9_or_less?).to eq(false)
+    end
+
+    it 'returns true when using PostgreSQL 9.6' do
+      allow(described_class).to receive(:postgresql?).and_return(true)
+      allow(described_class).to receive(:version).and_return('9.6')
+
+      expect(described_class.postgresql_9_or_less?).to eq(true)
+    end
+
+    it 'returns false when using PostgreSQL 10 or newer' do
+      allow(described_class).to receive(:postgresql?).and_return(true)
+      allow(described_class).to receive(:version).and_return('10')
+
+      expect(described_class.postgresql_9_or_less?).to eq(false)
+    end
+  end
+
   describe '.join_lateral_supported?' do
     it 'returns false when using MySQL' do
       allow(described_class).to receive(:postgresql?).and_return(false)
@@ -109,6 +131,70 @@ describe Gitlab::Database do
     end
   end
 
+  describe '.pg_wal_lsn_diff' do
+    it 'returns old name when using PostgreSQL 9.6' do
+      allow(described_class).to receive(:postgresql?).and_return(true)
+      allow(described_class).to receive(:version).and_return('9.6')
+
+      expect(described_class.pg_wal_lsn_diff).to eq('pg_xlog_location_diff')
+    end
+
+    it 'returns new name when using PostgreSQL 10 or newer' do
+      allow(described_class).to receive(:postgresql?).and_return(true)
+      allow(described_class).to receive(:version).and_return('10')
+
+      expect(described_class.pg_wal_lsn_diff).to eq('pg_wal_lsn_diff')
+    end
+  end
+
+  describe '.pg_current_wal_insert_lsn' do
+    it 'returns old name when using PostgreSQL 9.6' do
+      allow(described_class).to receive(:postgresql?).and_return(true)
+      allow(described_class).to receive(:version).and_return('9.6')
+
+      expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_xlog_insert_location')
+    end
+
+    it 'returns new name when using PostgreSQL 10 or newer' do
+      allow(described_class).to receive(:postgresql?).and_return(true)
+      allow(described_class).to receive(:version).and_return('10')
+
+      expect(described_class.pg_current_wal_insert_lsn).to eq('pg_current_wal_insert_lsn')
+    end
+  end
+
+  describe '.pg_last_wal_receive_lsn' do
+    it 'returns old name when using PostgreSQL 9.6' do
+      allow(described_class).to receive(:postgresql?).and_return(true)
+      allow(described_class).to receive(:version).and_return('9.6')
+
+      expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_xlog_receive_location')
+    end
+
+    it 'returns new name when using PostgreSQL 10 or newer' do
+      allow(described_class).to receive(:postgresql?).and_return(true)
+      allow(described_class).to receive(:version).and_return('10')
+
+      expect(described_class.pg_last_wal_receive_lsn).to eq('pg_last_wal_receive_lsn')
+    end
+  end
+
+  describe '.pg_last_wal_replay_lsn' do
+    it 'returns old name when using PostgreSQL 9.6' do
+      allow(described_class).to receive(:postgresql?).and_return(true)
+      allow(described_class).to receive(:version).and_return('9.6')
+
+      expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_xlog_replay_location')
+    end
+
+    it 'returns new name when using PostgreSQL 10 or newer' do
+      allow(described_class).to receive(:postgresql?).and_return(true)
+      allow(described_class).to receive(:version).and_return('10')
+
+      expect(described_class.pg_last_wal_replay_lsn).to eq('pg_last_wal_replay_lsn')
+    end
+  end
+
   describe '.nulls_last_order' do
     context 'when using PostgreSQL' do
       before do
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index b63658e1b3bad750108296b3cf476af7f7760d92..c5666e4ec61de63a210134832d92824a4e2e96d1 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -6,6 +6,31 @@ describe Gitlab::Git::Wiki do
   let(:project_wiki) { ProjectWiki.new(project, user) }
   subject { project_wiki.wiki }
 
+  describe '#pages' do
+    before do
+      create_page('page1', 'content')
+      create_page('page2', 'content2')
+    end
+
+    after do
+      destroy_page('page1')
+      destroy_page('page2')
+    end
+
+    it 'returns all the pages' do
+      expect(subject.pages.count).to eq(2)
+      expect(subject.pages.first.title).to eq 'page1'
+      expect(subject.pages.last.title).to eq 'page2'
+    end
+
+    it 'returns only one page' do
+      pages = subject.pages(limit: 1)
+
+      expect(pages.count).to eq(1)
+      expect(pages.first.title).to eq 'page1'
+    end
+  end
+
   describe '#page' do
     before do
       create_page('page1', 'content')
diff --git a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
index 6ad9f5ef76662807d21dedd92f2b37ce6e7648dc..5f67fe6b952625337c6ee6924907ce1c16c12b53 100644
--- a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb
@@ -70,6 +70,15 @@ describe Gitlab::GitalyClient::WikiService do
       subject
     end
 
+    it 'sends a limit of 0 to wiki_get_all_pages' do
+      expect_any_instance_of(Gitaly::WikiService::Stub)
+        .to receive(:wiki_get_all_pages)
+        .with(gitaly_request_with_params(limit: 0), kind_of(Hash))
+        .and_return([].each)
+
+      subject
+    end
+
     it 'concatenates the raw data and returns a pair of WikiPage and WikiPageVersion for each page' do
       expect_any_instance_of(Gitaly::WikiService::Stub)
         .to receive(:wiki_get_all_pages)
@@ -84,5 +93,18 @@ describe Gitlab::GitalyClient::WikiService do
       expect(wiki_page_2.raw_data).to eq('cd')
       expect(wiki_page_2_version.format).to eq('markdown')
     end
+
+    context 'with limits' do
+      subject { client.get_all_pages(limit: 1) }
+
+      it 'sends a request with the limit' do
+        expect_any_instance_of(Gitaly::WikiService::Stub)
+        .to receive(:wiki_get_all_pages)
+        .with(gitaly_request_with_params(limit: 1), kind_of(Hash))
+        .and_return([].each)
+
+        subject
+      end
+    end
   end
 end
diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb
index 50e8d7183ce0ef8abc6a9ba1a6124b2c741e20b3..22ad88e28cbbf50a5e7c1fb2ffc5a5c333793ed8 100644
--- a/spec/lib/gitlab/gitlab_import/client_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/client_spec.rb
@@ -15,4 +15,88 @@ describe Gitlab::GitlabImport::Client do
       expect(key).to be_kind_of(Symbol)
     end
   end
+
+  it 'uses membership and simple flags' do
+    stub_request('/api/v4/projects?membership=true&page=1&per_page=100&simple=true')
+
+    expect_any_instance_of(OAuth2::Response).to receive(:parsed).and_return([])
+
+    expect(client.projects.to_a).to eq []
+  end
+
+  shared_examples 'pagination params' do
+    before do
+      allow_any_instance_of(OAuth2::Response).to receive(:parsed).and_return([])
+    end
+
+    it 'allows page_limit param' do
+      allow_any_instance_of(OAuth2::Response).to receive(:parsed).and_return(element_list)
+
+      expect(client).to receive(:lazy_page_iterator).with(hash_including(page_limit: 2)).and_call_original
+
+      client.send(method, *args, page_limit: 2, per_page: 1).to_a
+    end
+
+    it 'allows per_page param' do
+      expect(client).to receive(:lazy_page_iterator).with(hash_including(per_page: 2)).and_call_original
+
+      client.send(method, *args, per_page: 2).to_a
+    end
+
+    it 'allows starting_page param' do
+      expect(client).to receive(:lazy_page_iterator).with(hash_including(starting_page: 3)).and_call_original
+
+      client.send(method, *args, starting_page: 3).to_a
+    end
+  end
+
+  describe '#projects' do
+    subject(:method) { :projects }
+    let(:args) { [] }
+    let(:element_list) { build_list(:project, 2) }
+
+    before do
+      stub_request('/api/v4/projects?membership=true&page=1&per_page=1&simple=true')
+      stub_request('/api/v4/projects?membership=true&page=2&per_page=1&simple=true')
+      stub_request('/api/v4/projects?membership=true&page=1&per_page=2&simple=true')
+      stub_request('/api/v4/projects?membership=true&page=3&per_page=100&simple=true')
+    end
+
+    it_behaves_like 'pagination params'
+  end
+
+  describe '#issues' do
+    subject(:method) { :issues }
+    let(:args) { [1] }
+    let(:element_list) { build_list(:issue, 2) }
+
+    before do
+      stub_request('/api/v4/projects/1/issues?page=1&per_page=1')
+      stub_request('/api/v4/projects/1/issues?page=2&per_page=1')
+      stub_request('/api/v4/projects/1/issues?page=1&per_page=2')
+      stub_request('/api/v4/projects/1/issues?page=3&per_page=100')
+    end
+
+    it_behaves_like 'pagination params'
+  end
+
+  describe '#issue_comments' do
+    subject(:method) { :issue_comments }
+    let(:args) { [1, 1] }
+    let(:element_list) { build_list(:note_on_issue, 2) }
+
+    before do
+      stub_request('/api/v4/projects/1/issues/1/notes?page=1&per_page=1')
+      stub_request('/api/v4/projects/1/issues/1/notes?page=2&per_page=1')
+      stub_request('/api/v4/projects/1/issues/1/notes?page=1&per_page=2')
+      stub_request('/api/v4/projects/1/issues/1/notes?page=3&per_page=100')
+    end
+
+    it_behaves_like 'pagination params'
+  end
+
+  def stub_request(path)
+    WebMock.stub_request(:get, "https://gitlab.com#{path}")
+      .to_return(status: 200)
+  end
 end
diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
index 813ae43b4d3288f5a7eba7ffb0ba5679889a9ec7..7eac2cacb90aaa392cc5650f2f64d024019d6e8b 100644
--- a/spec/lib/gitlab/hashed_storage/migrator_spec.rb
+++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
@@ -65,7 +65,7 @@ describe Gitlab::HashedStorage::Migrator do
     end
 
     it 'migrate project' do
-      Sidekiq::Testing.inline! do
+      perform_enqueued_jobs do
         subject.migrate(project)
       end
 
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 084ce3066d6901876ca1a606a8cc36fa5f637099..db5aab0cd76e24bd063b0ef51e624fa65a020017 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -211,6 +211,7 @@ project:
 - slack_service
 - microsoft_teams_service
 - mattermost_service
+- hangouts_chat_service
 - buildkite_service
 - bamboo_service
 - teamcity_service
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 20def4fefe2166462890e21b027e2a04ceca8358..a19b3c0ba666021f0d588b5e71a9dcd157d05a58 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -133,7 +133,7 @@ describe Gitlab::UsageData do
       expect(subject[:signup_enabled]).to eq(Gitlab::CurrentSettings.allow_signup?)
       expect(subject[:ldap_enabled]).to eq(Gitlab.config.ldap.enabled)
       expect(subject[:gravatar_enabled]).to eq(Gitlab::CurrentSettings.gravatar_enabled?)
-      expect(subject[:omniauth_enabled]).to eq(Gitlab.config.omniauth.enabled)
+      expect(subject[:omniauth_enabled]).to eq(Gitlab::Auth.omniauth_enabled?)
       expect(subject[:reply_by_email_enabled]).to eq(Gitlab::IncomingEmail.enabled?)
       expect(subject[:container_registry_enabled]).to eq(Gitlab.config.registry.enabled)
       expect(subject[:gitlab_shared_runners_enabled]).to eq(Gitlab.config.gitlab_ci.shared_runners_enabled)
diff --git a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
index 4395e2f82641a377cb1f335d87f7684a42e27249..5c6f213e15bbbcf54975a64cbca99fb4e5acb12a 100644
--- a/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
+++ b/spec/migrations/active_record/schedule_set_confidential_note_events_on_services_spec.rb
@@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnServices, :migration, :sidekiq do
   end
 
   it 'correctly processes services' do
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       expect(services_table.where(confidential_note_events: nil).count).to eq 4
       expect(services_table.where(confidential_note_events: true).count).to eq 1
 
diff --git a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
index a837498e1b1446c63b4810f95aa29f2490815df6..dd6f53257506aacf4044c21b74c7ee74671b22c7 100644
--- a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
+++ b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
@@ -44,7 +44,7 @@ describe MigrateStageIdReferenceInBackground, :migration, :sidekiq do
   end
 
   it 'schedules background migrations' do
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       expect(jobs.where(stage_id: nil).count).to eq 5
 
       migrate!
diff --git a/spec/migrations/migrate_stages_statuses_spec.rb b/spec/migrations/migrate_stages_statuses_spec.rb
index ce35276cbf5bb8812c69fb8660b3c0d6c263a4bc..5483e24fce7c6570029ac59c40bb9a958d22f36d 100644
--- a/spec/migrations/migrate_stages_statuses_spec.rb
+++ b/spec/migrations/migrate_stages_statuses_spec.rb
@@ -34,7 +34,7 @@ describe MigrateStagesStatuses, :sidekiq, :migration do
   end
 
   it 'correctly migrates stages statuses' do
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       expect(stages.where(status: nil).count).to eq 3
 
       migrate!
diff --git a/spec/migrations/normalize_ldap_extern_uids_spec.rb b/spec/migrations/normalize_ldap_extern_uids_spec.rb
index 56a78f52802f2795c65e5fb3a7a30d91f134a474..c6ea1e3e49e19a2e3a1ea135fadf0628e9e6ea53 100644
--- a/spec/migrations/normalize_ldap_extern_uids_spec.rb
+++ b/spec/migrations/normalize_ldap_extern_uids_spec.rb
@@ -38,7 +38,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do
   end
 
   it 'migrates the LDAP identities' do
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       migrate!
       identities.where(id: 1..4).each do |identity|
         expect(identity.extern_uid).to eq("uid=foo #{identity.id},ou=people,dc=example,dc=com")
@@ -47,7 +47,7 @@ describe NormalizeLdapExternUids, :migration, :sidekiq do
   end
 
   it 'does not modify non-LDAP identities' do
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       migrate!
       identity = identities.last
       expect(identity.extern_uid).to eq(" uid = foo 5, ou = People, dc = example, dc = com ")
diff --git a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
index ed306fb3d62c0ec05c500f8613fe9e1742750879..96bef1075990d1a2b7abc71ef295f4a4a764761e 100644
--- a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
+++ b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
@@ -20,7 +20,7 @@ describe ScheduleCreateGpgKeySubkeysFromGpgKeys, :migration, :sidekiq do
   end
 
   it 'schedules background migrations' do
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       expect(GpgKeySubkey.count).to eq(0)
 
       migrate!
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
index d230f06444424e838201c21c5febf06736682834..9f7e47bae0dda2168bf0cca54a71995a295835f4 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
@@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrations, :migration, :sidekiq do
   end
 
   it 'schedules background migrations' do
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL'
 
       expect(merge_request_diffs.where(non_empty).count).to eq 3
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
index 1aab4ae165084dce9555cf02126c52717f4469aa..5bcb923af7befbf98c2aea10785302ffb269b996 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
@@ -33,7 +33,7 @@ describe ScheduleMergeRequestDiffMigrationsTakeTwo, :migration, :sidekiq do
   end
 
   it 'migrates the data' do
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       non_empty = 'st_commits IS NOT NULL OR st_diffs IS NOT NULL'
 
       expect(merge_request_diffs.where(non_empty).count).to eq 3
diff --git a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
index c9fdbe95d13b7e5e43069fcef5dc61e2271aa992..76fe16581ac8c5a9b97a6ca550f74bf3683a0c69 100644
--- a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
@@ -53,7 +53,7 @@ describe ScheduleMergeRequestLatestMergeRequestDiffIdMigrations, :migration, :si
   end
 
   it 'schedules background migrations' do
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       expect(merge_requests_table.where(latest_merge_request_diff_id: nil).count).to eq 3
 
       migrate!
diff --git a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
index 027f4a91c90c107d2721692b35f9b9b032d37705..fa4ddd5fbc7220e576263900ccf78d84da6458ee 100644
--- a/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
+++ b/spec/migrations/schedule_set_confidential_note_events_on_webhooks_spec.rb
@@ -31,7 +31,7 @@ describe ScheduleSetConfidentialNoteEventsOnWebhooks, :migration, :sidekiq do
   end
 
   it 'correctly processes web hooks' do
-    Sidekiq::Testing.inline! do
+    perform_enqueued_jobs do
       expect(web_hooks_table.where(confidential_note_events: nil).count).to eq 4
       expect(web_hooks_table.where(confidential_note_events: true).count).to eq 1
 
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 774a638b430bed62da15678ed90da577f7e1f4e1..915bf134d579b83ebaf60c8276c0bb0a45aa87c3 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -179,7 +179,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
         end
 
         it 'migrates data to object storage' do
-          Sidekiq::Testing.inline! do
+          perform_enqueued_jobs do
             subject
 
             build_trace_chunk.reload
@@ -201,7 +201,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
         end
 
         it 'does not migrate data to object storage' do
-          Sidekiq::Testing.inline! do
+          perform_enqueued_jobs do
             data_store = build_trace_chunk.data_store
 
             subject
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index f8d51a9583378126711b23658958c36964956464..cd84a684fec3af1207498de814656240efa313dd 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -62,11 +62,18 @@ describe DeployToken do
       end
     end
 
-    context "when it hasn't been revoked" do
+    context "when it hasn't been revoked and is not expired" do
       it 'should return true' do
         expect(deploy_token.active?).to be_truthy
       end
     end
+
+    context "when it hasn't been revoked and is expired" do
+      it 'should return true' do
+        deploy_token.update_attribute(:expires_at, Date.today - 5.days)
+        expect(deploy_token.active?).to be_falsy
+      end
+    end
   end
 
   describe '#username' do
diff --git a/spec/models/project_services/hangouts_chat_service_spec.rb b/spec/models/project_services/hangouts_chat_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cfa55188a647a247bda9b5d476561fdeb8ba14be
--- /dev/null
+++ b/spec/models/project_services/hangouts_chat_service_spec.rb
@@ -0,0 +1,246 @@
+require 'spec_helper'
+
+describe HangoutsChatService 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 do
+        subject.active = true
+      end
+
+      it { is_expected.to validate_presence_of(:webhook) }
+      it_behaves_like 'issue tracker service URL attribute', :webhook
+    end
+
+    context 'when service is inactive' do
+      before do
+        subject.active = false
+      end
+
+      it { is_expected.not_to validate_presence_of(:webhook) }
+    end
+  end
+
+  describe '#execute' do
+    let(:user)    { create(:user) }
+    let(:project) { create(:project, :repository) }
+    let(:webhook_url) { 'https://example.gitlab.com/' }
+
+    before do
+      allow(subject).to receive_messages(
+        project: project,
+        project_id: project.id,
+        service_hook: true,
+        webhook: webhook_url
+      )
+
+      WebMock.stub_request(:post, webhook_url)
+    end
+
+    shared_examples 'Hangouts Chat service' do
+      it 'calls Hangouts Chat API' do
+        subject.execute(sample_data)
+
+        expect(WebMock)
+          .to have_requested(:post, webhook_url)
+          .with { |req| req.body =~ /\A{"text":.+}\Z/ }
+          .once
+      end
+    end
+
+    context 'with push events' do
+      let(:sample_data) do
+        Gitlab::DataBuilder::Push.build_sample(project, user)
+      end
+
+      it_behaves_like 'Hangouts Chat service'
+
+      it 'specifies the webhook when it is configured' do
+        expect(HangoutsChat::Sender).to receive(:new).with(webhook_url).and_return(double(:hangouts_chat_service).as_null_object)
+
+        subject.execute(sample_data)
+      end
+
+      context 'with not default branch' do
+        let(:sample_data) do
+          Gitlab::DataBuilder::Push.build(project, user, nil, nil, 'not-the-default-branch')
+        end
+
+        context 'when notify_only_default_branch enabled' do
+          before do
+            subject.notify_only_default_branch = true
+          end
+
+          it 'does not call the Hangouts Chat API' do
+            result = subject.execute(sample_data)
+
+            expect(result).to be_falsy
+          end
+        end
+
+        context 'when notify_only_default_branch disabled' do
+          before do
+            subject.notify_only_default_branch = false
+          end
+
+          it_behaves_like 'Hangouts Chat service'
+        end
+      end
+    end
+
+    context 'with issue events' do
+      let(:opts) { { title: 'Awesome issue', description: 'please fix' } }
+      let(:sample_data) do
+        service = Issues::CreateService.new(project, user, opts)
+        issue = service.execute
+        service.hook_data(issue, 'open')
+      end
+
+      it_behaves_like 'Hangouts Chat service'
+    end
+
+    context 'with merge events' do
+      let(:opts) do
+        {
+          title: 'Awesome merge_request',
+          description: 'please fix',
+          source_branch: 'feature',
+          target_branch: 'master'
+        }
+      end
+
+      let(:sample_data) do
+        service = MergeRequests::CreateService.new(project, user, opts)
+        merge_request = service.execute
+        service.hook_data(merge_request, 'open')
+      end
+
+      before do
+        project.add_developer(user)
+      end
+
+      it_behaves_like 'Hangouts Chat service'
+    end
+
+    context 'with wiki page events' do
+      let(:opts) do
+        {
+          title: 'Awesome wiki_page',
+          content: 'Some text describing some thing or another',
+          format: 'md',
+          message: 'user created page: Awesome wiki_page'
+        }
+      end
+      let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) }
+      let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, 'create') }
+
+      it_behaves_like 'Hangouts Chat service'
+    end
+
+    context 'with note events' do
+      let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) }
+
+      context 'with commit comment' do
+        let(:note) do
+          create(:note_on_commit, author: user,
+                                  project: project,
+                                  commit_id: project.repository.commit.id,
+                                  note: 'a comment on a commit')
+        end
+
+        it_behaves_like 'Hangouts Chat service'
+      end
+
+      context 'with merge request comment' do
+        let(:note) do
+          create(:note_on_merge_request, project: project,
+                                         note: 'merge request note')
+        end
+
+        it_behaves_like 'Hangouts Chat service'
+      end
+
+      context 'with issue comment' do
+        let(:note) do
+          create(:note_on_issue, project: project, note: 'issue note')
+        end
+
+        it_behaves_like 'Hangouts Chat service'
+      end
+
+      context 'with snippet comment' do
+        let(:note) do
+          create(:note_on_project_snippet, project: project,
+                                           note: 'snippet note')
+        end
+
+        it_behaves_like 'Hangouts Chat service'
+      end
+    end
+
+    context 'with pipeline events' do
+      let(:pipeline) do
+        create(:ci_pipeline,
+               project: project, status: status,
+               sha: project.commit.sha, ref: project.default_branch)
+      end
+      let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
+
+      context 'with failed pipeline' do
+        let(:status) { 'failed' }
+
+        it_behaves_like 'Hangouts Chat service'
+      end
+
+      context 'with succeeded pipeline' do
+        let(:status) { 'success' }
+
+        context 'with default notify_only_broken_pipelines' do
+          it 'does not call Hangouts Chat API' do
+            result = subject.execute(sample_data)
+
+            expect(result).to be_falsy
+          end
+        end
+
+        context 'when notify_only_broken_pipelines is false' do
+          before do
+            subject.notify_only_broken_pipelines = false
+          end
+
+          it_behaves_like 'Hangouts Chat service'
+        end
+      end
+
+      context 'with not default branch' do
+        let(:pipeline) do
+          create(:ci_pipeline, project: project, status: 'failed', ref: 'not-the-default-branch')
+        end
+
+        context 'when notify_only_default_branch enabled' do
+          before do
+            subject.notify_only_default_branch = true
+          end
+
+          it 'does not call the Hangouts Chat API' do
+            result = subject.execute(sample_data)
+
+            expect(result).to be_falsy
+          end
+        end
+
+        context 'when notify_only_default_branch disabled' do
+          before do
+            subject.notify_only_default_branch = false
+          end
+
+          it_behaves_like 'Hangouts Chat service'
+        end
+      end
+    end
+  end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index d200e5f2e4282f64369a4dd63380a6a788cff493..b0ec725bf70f42d6368fd74ad39d597b7afe73c3 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -26,6 +26,7 @@ describe Project do
     it { is_expected.to have_one(:slack_service) }
     it { is_expected.to have_one(:microsoft_teams_service) }
     it { is_expected.to have_one(:mattermost_service) }
+    it { is_expected.to have_one(:hangouts_chat_service) }
     it { is_expected.to have_one(:packagist_service) }
     it { is_expected.to have_one(:pushover_service) }
     it { is_expected.to have_one(:asana_service) }
diff --git a/spec/models/spam_log_spec.rb b/spec/models/spam_log_spec.rb
index 0d6b4384adaa216e7b958520b2f08ed82699c7ac..90a2caaeb88e83f1176b287475aad977fc41e94f 100644
--- a/spec/models/spam_log_spec.rb
+++ b/spec/models/spam_log_spec.rb
@@ -22,7 +22,7 @@ describe SpamLog do
       spam_log = build(:spam_log)
       user = spam_log.user
 
-      Sidekiq::Testing.inline! do
+      perform_enqueued_jobs do
         spam_log.remove_user(deleted_by: admin)
       end
 
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index bf93555b0f0d9d162fe66dbfccc1cc3c5501c76e..f3db0c122a0a403372cd13674498f626d7d6da47 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -20,7 +20,7 @@ describe API::Environments do
           path path_with_namespace
           star_count forks_count
           created_at last_activity_at
-          avatar_url
+          avatar_url namespace
         )
 
         get api("/projects/#{project.id}/environments", user)
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 65b387a21707e1be0f3fbb216760bc9188d511bf..3a8948f84770193bafa3078c70789526b2e056af 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -226,6 +226,25 @@ describe API::Groups do
         expect(json_response.first['name']).to eq(group2.name)
       end
     end
+
+    context 'when using min_access_level in the request' do
+      let!(:group3) { create(:group, :private) }
+      let(:response_groups) { json_response.map { |group| group['id'] } }
+
+      before do
+        group1.add_developer(user2)
+        group3.add_master(user2)
+      end
+
+      it 'returns an array of groups the user has at least master access' do
+        get api('/groups', user2), min_access_level: 40
+
+        expect(response).to have_gitlab_http_status(200)
+        expect(response).to include_pagination_headers
+        expect(json_response).to be_an Array
+        expect(response_groups).to eq([group2.id, group3.id])
+      end
+    end
   end
 
   describe "GET /groups/:id" do
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 7d1a5c1280549fe997229ee4448894b843620ffa..8412d0383f731e1b414f4a7bb1abd10aa5990954 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -220,6 +220,7 @@ describe API::Jobs do
         expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at)
         expect(Time.parse(json_response['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
         expect(json_response['duration']).to eq(job.duration)
+        expect(json_response['web_url']).to be_present
       end
 
       it 'returns pipeline data' do
diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb
index e2ca27f5d41cca0df473306df965e6ad7e79e8e6..342a97b6a69eaf68acb8617928ca412a3b72edbe 100644
--- a/spec/requests/api/pipelines_spec.rb
+++ b/spec/requests/api/pipelines_spec.rb
@@ -24,7 +24,8 @@ describe API::Pipelines do
         expect(json_response).to be_an Array
         expect(json_response.first['sha']).to match /\A\h{40}\z/
         expect(json_response.first['id']).to eq pipeline.id
-        expect(json_response.first.keys).to contain_exactly(*%w[id sha ref status])
+        expect(json_response.first['web_url']).to be_present
+        expect(json_response.first.keys).to contain_exactly(*%w[id sha ref status web_url])
       end
 
       context 'when parameter is passed' do
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index 41243854ebc06dd1b860d42e5984b39b67e6b8f6..55332f56508444cb47cce68728f86ed0bbae6294 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -102,7 +102,7 @@ describe API::ProjectImport do
     it 'correctly overrides params during the import' do
       override_params = { 'description' => 'Hello world' }
 
-      Sidekiq::Testing.inline! do
+      perform_enqueued_jobs do
         post api('/projects/import', user),
              path: 'test-import',
              file: fixture_file_upload(file),
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index f72c01561d8f6c88d4812207448894cb3e266199..71e3436fa76046885edec03d3539660d5ba80af3 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -225,7 +225,7 @@ describe API::Projects do
             path path_with_namespace
             star_count forks_count
             created_at last_activity_at
-            avatar_url
+            avatar_url namespace
           )
 
           get api('/projects?simple=true', user)
@@ -400,6 +400,22 @@ describe API::Projects do
           end
         end
       end
+
+      context 'and with min_access_level' do
+        before do
+          project2.add_master(user2)
+          project3.add_developer(user2)
+          project4.add_reporter(user2)
+        end
+
+        it 'returns an array of groups the user has at least developer access' do
+          get api('/projects', user2), { min_access_level: 30 }
+          expect(response).to have_gitlab_http_status(200)
+          expect(response).to include_pagination_headers
+          expect(json_response).to be_an Array
+          expect(json_response.map { |project| project['id'] }).to contain_exactly(project2.id, project3.id)
+        end
+      end
     end
 
     context 'when authenticated as a different user' do
@@ -681,6 +697,20 @@ describe API::Projects do
       expect(json_response).to be_an Array
       expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
     end
+
+    it 'returns projects filetered by minimal access level' do
+      private_project1 = create(:project, :private, name: 'private_project1', creator_id: user4.id, namespace: user4.namespace)
+      private_project2 = create(:project, :private, name: 'private_project2', creator_id: user4.id, namespace: user4.namespace)
+      private_project1.add_developer(user2)
+      private_project2.add_reporter(user2)
+
+      get api("/users/#{user4.id}/projects/", user2), { min_access_level: 30 }
+
+      expect(response).to have_gitlab_http_status(200)
+      expect(response).to include_pagination_headers
+      expect(json_response).to be_an Array
+      expect(json_response.map { |project| project['id'] }).to contain_exactly(private_project1.id)
+    end
   end
 
   describe 'POST /projects/user/:id' do
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index a97c3f3461a584a98040eaf43be31cc5ad8a98f3..6a051f865aa6df1b34d27bedd201534d4d659860 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -11,6 +11,7 @@ describe API::Users do
   let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
   let(:not_existing_user_id) { (User.maximum('id') || 0 ) + 10 }
   let(:not_existing_pat_id) { (PersonalAccessToken.maximum('id') || 0 ) + 10 }
+  let(:private_user) { create(:user, private_profile: true) }
 
   describe 'GET /users' do
     context "when unauthenticated" do
@@ -254,6 +255,13 @@ describe API::Users do
         expect(response).to match_response_schema('public_api/v4/user/admin')
         expect(json_response['is_admin']).to be(false)
       end
+
+      it "includes the `created_at` field for private users" do
+        get api("/users/#{private_user.id}", admin)
+
+        expect(response).to match_response_schema('public_api/v4/user/admin')
+        expect(json_response.keys).to include 'created_at'
+      end
     end
 
     context 'for an anonymous user' do
@@ -272,6 +280,20 @@ describe API::Users do
 
         expect(response).to have_gitlab_http_status(404)
       end
+
+      it "returns the `created_at` field for public users" do
+        get api("/users/#{user.id}")
+
+        expect(response).to match_response_schema('public_api/v4/user/basic')
+        expect(json_response.keys).to include 'created_at'
+      end
+
+      it "does not return the `created_at` field for private users" do
+        get api("/users/#{private_user.id}")
+
+        expect(response).to match_response_schema('public_api/v4/user/basic')
+        expect(json_response.keys).not_to include 'created_at'
+      end
     end
 
     it "returns a 404 error if user id not found" do
@@ -374,6 +396,18 @@ describe API::Users do
       expect(new_user.recently_sent_password_reset?).to eq(true)
     end
 
+    it "creates user with private profile" do
+      post api('/users', admin), attributes_for(:user, private_profile: true)
+
+      expect(response).to have_gitlab_http_status(201)
+
+      user_id = json_response['id']
+      new_user = User.find(user_id)
+
+      expect(new_user).not_to eq(nil)
+      expect(new_user.private_profile?).to eq(true)
+    end
+
     it "does not create user with invalid email" do
       post api('/users', admin),
            email: 'invalid email',
@@ -583,6 +617,13 @@ describe API::Users do
       expect(user.reload.external?).to be_truthy
     end
 
+    it "updates private profile" do
+      put api("/users/#{user.id}", admin), { private_profile: true }
+
+      expect(response).to have_gitlab_http_status(200)
+      expect(user.reload.private_profile).to eq(true)
+    end
+
     it "does not update admin status" do
       put api("/users/#{admin_user.id}", admin), { can_create_group: false }
 
@@ -1067,7 +1108,7 @@ describe API::Users do
     end
 
     it "deletes user" do
-      Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
+      perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
 
       expect(response).to have_gitlab_http_status(204)
       expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
@@ -1079,30 +1120,30 @@ describe API::Users do
     end
 
     it "does not delete for unauthenticated user" do
-      Sidekiq::Testing.inline! { delete api("/users/#{user.id}") }
+      perform_enqueued_jobs { delete api("/users/#{user.id}") }
       expect(response).to have_gitlab_http_status(401)
     end
 
     it "is not available for non admin users" do
-      Sidekiq::Testing.inline! { delete api("/users/#{user.id}", user) }
+      perform_enqueued_jobs { delete api("/users/#{user.id}", user) }
       expect(response).to have_gitlab_http_status(403)
     end
 
     it "returns 404 for non-existing user" do
-      Sidekiq::Testing.inline! { delete api("/users/999999", admin) }
+      perform_enqueued_jobs { delete api("/users/999999", admin) }
       expect(response).to have_gitlab_http_status(404)
       expect(json_response['message']).to eq('404 User Not Found')
     end
 
     it "returns a 404 for invalid ID" do
-      Sidekiq::Testing.inline! { delete api("/users/ASDF", admin) }
+      perform_enqueued_jobs { delete api("/users/ASDF", admin) }
 
       expect(response).to have_gitlab_http_status(404)
     end
 
     context "hard delete disabled" do
       it "moves contributions to the ghost user" do
-        Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
+        perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
 
         expect(response).to have_gitlab_http_status(204)
         expect(issue.reload).to be_persisted
@@ -1112,7 +1153,7 @@ describe API::Users do
 
     context "hard delete enabled" do
       it "removes contributions" do
-        Sidekiq::Testing.inline! { delete api("/users/#{user.id}?hard_delete=true", admin) }
+        perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
 
         expect(response).to have_gitlab_http_status(204)
         expect(Issue.exists?(issue.id)).to be_falsy
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 0f3e7157e14ccace478ccc9c800007a3bd0b7017..c71eae9164ac5c903fe6afafdf2b141c843d9d80 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -381,6 +381,10 @@ describe 'Git HTTP requests' do
 
           context "when authentication fails" do
             context "when the user is IP banned" do
+              before do
+                Gitlab.config.rack_attack.git_basic_auth['enabled'] = true
+              end
+
               it "responds with status 401" do
                 expect(Rack::Attack::Allow2Ban).to receive(:filter).and_return(true)
                 allow_any_instance_of(Rack::Request).to receive(:ip).and_return('1.2.3.4')
@@ -420,6 +424,10 @@ describe 'Git HTTP requests' do
               end
 
               context "when the user isn't blocked" do
+                before do
+                  Gitlab.config.rack_attack.git_basic_auth['enabled'] = true
+                end
+
                 it "resets the IP in Rack Attack on download" do
                   expect(Rack::Attack::Allow2Ban).to receive(:reset).twice
 
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index de39abdb746d26fb0fb8dc7749825c358a6925ef..c2378646f8955525f335479a105daeedf43ae903 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -575,6 +575,40 @@ describe 'Git LFS API and storage' do
         end
       end
 
+      context 'when using Deploy Tokens' do
+        let(:project) { create(:project, :repository) }
+        let(:authorization) { authorize_deploy_token }
+        let(:update_user_permissions) { nil }
+        let(:role) { nil }
+        let(:update_lfs_permissions) do
+          project.lfs_objects << lfs_object
+        end
+
+        context 'when Deploy Token is valid' do
+          let(:deploy_token) { create(:deploy_token, projects: [project]) }
+
+          it_behaves_like 'an authorized requests'
+        end
+
+        context 'when Deploy Token is not valid' do
+          let(:deploy_token) { create(:deploy_token, projects: [project], read_repository: false) }
+
+          it 'responds with access denied' do
+            expect(response).to have_gitlab_http_status(401)
+          end
+        end
+
+        context 'when Deploy Token is not related to the project' do
+          let(:another_project) { create(:project, :repository) }
+          let(:deploy_token) { create(:deploy_token, projects: [another_project]) }
+
+          it 'responds with access forbidden' do
+            # We render 404, to prevent data leakage about existence of the project
+            expect(response).to have_gitlab_http_status(404)
+          end
+        end
+      end
+
       context 'when build is authorized as' do
         let(:authorization) { authorize_ci_project }
 
@@ -1381,6 +1415,10 @@ describe 'Git LFS API and storage' do
     ActionController::HttpAuthentication::Basic.encode_credentials(user.username, Gitlab::LfsToken.new(user).token)
   end
 
+  def authorize_deploy_token
+    ActionController::HttpAuthentication::Basic.encode_credentials(deploy_token.username, deploy_token.token)
+  end
+
   def post_lfs_json(url, body = nil, headers = nil)
     post(url, body.try(:to_json), (headers || {}).merge('Content-Type' => LfsRequest::CONTENT_TYPE))
   end
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index a9baccd061a1a2acaf2487fd880a70b0aced3dc0..b54491cf5f9c98e5ab22266517b0fa05f1036a54 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -49,7 +49,7 @@ describe Groups::DestroyService do
       context 'Sidekiq inline' do
         before do
           # Run sidekiq immediately to check that renamed dir will be removed
-          Sidekiq::Testing.inline! { destroy_group(group, user, async) }
+          perform_enqueued_jobs { destroy_group(group, user, async) }
         end
 
         it 'verifies that paths have been deleted' do
diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb
index 9aa9237d875801ae0502a266e36f00cccc5fd10e..a43da01f37e44ce066330522a9876bfe6c01dbe4 100644
--- a/spec/services/projects/create_from_template_service_spec.rb
+++ b/spec/services/projects/create_from_template_service_spec.rb
@@ -28,7 +28,7 @@ describe Projects::CreateFromTemplateService do
 
   context 'the result project' do
     before do
-      Sidekiq::Testing.inline! do
+      perform_enqueued_jobs do
         @project = subject.execute
       end
 
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 38660ad7a0133283ef051274d975501cdbda43ce..e428808ab686f48eecf57cfe4b776102c680a02c 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -45,18 +45,18 @@ describe Projects::DestroyService do
   shared_examples 'handles errors thrown during async destroy' do |error_message|
     it 'does not allow the error to bubble up' do
       expect do
-        Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+        perform_enqueued_jobs { destroy_project(project, user, {}) }
       end.not_to raise_error
     end
 
     it 'unmarks the project as "pending deletion"' do
-      Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+      perform_enqueued_jobs { destroy_project(project, user, {}) }
 
       expect(project.reload.pending_delete).to be(false)
     end
 
     it 'stores an error message in `projects.delete_error`' do
-      Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+      perform_enqueued_jobs { destroy_project(project, user, {}) }
 
       expect(project.reload.delete_error).to be_present
       expect(project.delete_error).to include(error_message)
@@ -66,7 +66,7 @@ describe Projects::DestroyService do
   context 'Sidekiq inline' do
     before do
       # Run sidekiq immediatly to check that renamed repository will be removed
-      Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+      perform_enqueued_jobs { destroy_project(project, user, {}) }
     end
 
     context 'when has remote mirrors' do
@@ -110,7 +110,7 @@ describe Projects::DestroyService do
     end
 
     it 'keeps project team intact upon an error' do
-      Sidekiq::Testing.inline! do
+      perform_enqueued_jobs do
         begin
           destroy_project(project, user, {})
         rescue ::Redis::CannotConnectError
@@ -128,7 +128,7 @@ describe Projects::DestroyService do
       before do
         project.project_feature.update_attribute("issues_access_level", ProjectFeature::PRIVATE)
         # Run sidekiq immediately to check that renamed repository will be removed
-        Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+        perform_enqueued_jobs { destroy_project(project, user, {}) }
       end
 
       it_behaves_like 'deleting the project'
@@ -172,7 +172,7 @@ describe Projects::DestroyService do
 
         it 'allows error to bubble up and rolls back project deletion' do
           expect do
-            Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
+            perform_enqueued_jobs { destroy_project(project, user, {}) }
           end.to raise_error(Exception, 'Other error message')
 
           expect(project.reload.pending_delete).to be(false)
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index 1cf373d1d72450b777612028cf88560f54d68965..18ecef1c0a18e7420928cfe42c3e144fc9500edf 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -35,7 +35,7 @@ describe Projects::HousekeepingService do
       allow(subject).to receive(:gc_period).and_return(1)
       project.increment_pushes_since_gc
 
-      Sidekiq::Testing.inline! do
+      perform_enqueued_jobs do
         expect { subject.execute }.to change { project.pushes_since_gc }.to(0)
       end
     end
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index b381504579223f1cc39769f31be7b0507c30d029..e2a600d12d1b3b2126244b0816536e216e8e80cb 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -69,7 +69,7 @@ describe Projects::ImportService do
         result = subject.execute
 
         expect(result[:status]).to eq :error
-        expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - The repository could not be created."
+        expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - The repository could not be created."
       end
 
       context 'when repository creation succeeds' do
@@ -141,7 +141,7 @@ describe Projects::ImportService do
           result = subject.execute
 
           expect(result[:status]).to eq :error
-          expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository"
+          expect(result[:message]).to eq "Error importing repository #{project.safe_import_url} into #{project.full_path} - Failed to import the repository"
         end
 
         context 'when repository import scheduled' do
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index f82d4b483e7cb55447acdcd9899bd3b5575d2c58..3bae8bfbd42430e8c41b233b368a2af269d3bbd9 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -173,7 +173,7 @@ describe Users::DestroyService do
 
     describe "user personal's repository removal" do
       before do
-        Sidekiq::Testing.inline! { service.execute(user) }
+        perform_enqueued_jobs { service.execute(user) }
       end
 
       context 'legacy storage' do
diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb
index 815432aacce7295fac8d22d07035fa6e131d3426..808084c8f7c4e16e61e2d43f6507c98627b8520e 100644
--- a/spec/workers/storage_migrator_worker_spec.rb
+++ b/spec/workers/storage_migrator_worker_spec.rb
@@ -13,7 +13,7 @@ describe StorageMigratorWorker do
     end
 
     it 'migrates projects in the specified range' do
-      Sidekiq::Testing.inline! do
+      perform_enqueued_jobs do
         worker.perform(ids.min, ids.max)
       end