diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index cb4bd0ad5f56972902fbcc4d65fe740cc707e397..603a51266da1c37d6e503267fbd6c745b25f79c3 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -80,10 +80,6 @@ class Projects::ApplicationController < ApplicationController
     cookies.permanent[:diff_view] = params.delete(:view) if params[:view].present?
   end
 
-  def builds_enabled
-    return render_404 unless @project.feature_available?(:builds, current_user)
-  end
-
   def require_pages_enabled!
     not_found unless Gitlab.config.pages.enabled
   end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 6b4d9685a27302bdd9137b57a200846a355f4140..fb1d4aed58bcf05e32faa90a795f198963440013 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -101,9 +101,10 @@ class GitPushService < BaseService
     UpdateMergeRequestsWorker
       .perform_async(@project.id, current_user.id, params[:oldrev], params[:newrev], params[:ref])
 
-    SystemHookPushWorker.perform_async(build_push_data.dup, :push_hooks)
-    Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute(:push)
     EventCreateService.new.push(@project, current_user, build_push_data)
+    Ci::CreatePipelineService.new(@project, current_user, build_push_data).execute(:push)
+    
+    SystemHookPushWorker.perform_async(build_push_data.dup, :push_hooks)
     @project.execute_hooks(build_push_data.dup, :push_hooks)
     @project.execute_services(build_push_data.dup, :push_hooks)
 
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index 8710c3999b312ac3e5012a8c5fb2c882c9c55129..9917a39b795997dc2f649c3ba56c80588e5c338d 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -9,9 +9,11 @@ class GitTagPushService < BaseService
 
     EventCreateService.new.push(project, current_user, @push_data)
     Ci::CreatePipelineService.new(project, current_user, @push_data).execute(:push)
+
     SystemHooksService.new.execute_hooks(build_system_push_data.dup, :tag_push_hooks)
     project.execute_hooks(@push_data.dup, :tag_push_hooks)
     project.execute_services(@push_data.dup, :tag_push_hooks)
+
     ProjectCacheWorker.perform_async(project.id, [], [:commit_count, :repository_size])
 
     true
diff --git a/changelogs/unreleased/fix-support-for-external-ci-services.yml b/changelogs/unreleased/fix-support-for-external-ci-services.yml
new file mode 100644
index 0000000000000000000000000000000000000000..eecb451925995c5569a446dd31dae9a46fab83f9
--- /dev/null
+++ b/changelogs/unreleased/fix-support-for-external-ci-services.yml
@@ -0,0 +1,4 @@
+---
+title: Fix support for external CI services
+merge_request: 11176
+author:
diff --git a/lib/gitlab/ci/status/external/common.rb b/lib/gitlab/ci/status/external/common.rb
index cb0fc94f9ae16eb1ec12904fa099e5e35f3bcc39..9307545b5b16d40c1dc2b1f792ee1c1c29ee2162 100644
--- a/lib/gitlab/ci/status/external/common.rb
+++ b/lib/gitlab/ci/status/external/common.rb
@@ -4,7 +4,7 @@ module Gitlab
       module External
         module Common
           def label
-            subject.description || super
+            subject.description
           end
 
           def has_details?
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 954f89e38546a1f4ae4a1039c9768bfa54d580cc..734532668d38d9af4a76184733fb23d24a35baae 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -5,9 +5,12 @@ describe Projects::PipelinesController do
 
   let(:user) { create(:user) }
   let(:project) { create(:empty_project, :public) }
+  let(:feature) { ProjectFeature::DISABLED }
 
   before do
     project.add_developer(user)
+    project.project_feature.update(
+      builds_access_level: feature)
 
     sign_in(user)
   end
@@ -153,16 +156,26 @@ describe Projects::PipelinesController do
                    format: :json
     end
 
-    it 'retries a pipeline without returning any content' do
-      expect(response).to have_http_status(:no_content)
-      expect(build.reload).to be_retried
+    context 'when builds are enabled' do
+      let(:feature) { ProjectFeature::ENABLED }
+  
+      it 'retries a pipeline without returning any content' do
+        expect(response).to have_http_status(:no_content)
+        expect(build.reload).to be_retried
+      end
+    end
+
+    context 'when builds are disabled' do
+      it 'fails to retry pipeline' do
+        expect(response).to have_http_status(:not_found)
+      end
     end
   end
 
   describe 'POST cancel.json' do
     let!(:pipeline) { create(:ci_pipeline, project: project) }
     let!(:build) { create(:ci_build, :running, pipeline: pipeline) }
-
+  
     before do
       post :cancel, namespace_id: project.namespace,
                     project_id: project,
@@ -170,9 +183,19 @@ describe Projects::PipelinesController do
                     format: :json
     end
 
-    it 'cancels a pipeline without returning any content' do
-      expect(response).to have_http_status(:no_content)
-      expect(pipeline.reload).to be_canceled
+    context 'when builds are enabled' do
+      let(:feature) { ProjectFeature::ENABLED }
+  
+      it 'cancels a pipeline without returning any content' do
+        expect(response).to have_http_status(:no_content)
+        expect(pipeline.reload).to be_canceled
+      end
+    end
+
+    context 'when builds are disabled' do
+      it 'fails to retry pipeline' do
+        expect(response).to have_http_status(:not_found)
+      end
     end
   end
 end
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index c49648f54bdcab621a28870fc91602ae9454c6d7..d76b5e4ef1b0dbb6d5d3535e218169a5c1767cb2 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -68,9 +68,12 @@ describe 'Edit Project Settings', feature: true do
   end
 
   describe 'project features visibility pages' do
+    let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+    let(:job) { create(:ci_build, pipeline: pipeline) }
+
     let(:tools) do
       {
-        builds: namespace_project_pipelines_path(project.namespace, project),
+        builds: namespace_project_job_path(project.namespace, project, job),
         issues: namespace_project_issues_path(project.namespace, project),
         wiki: namespace_project_wiki_path(project.namespace, project, :home),
         snippets: namespace_project_snippets_path(project.namespace, project),
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index a695621b87a2499a58aa40d169925668aa34c685..3ed0b0a756bd6fa40c370d8ca4926a47648d6ae3 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -300,4 +300,37 @@ describe ProjectsHelper do
       expect(helper.send(:visibility_select_options, project, Gitlab::VisibilityLevel::PRIVATE)).to include('Private')
     end
   end
+
+  describe '#get_project_nav_tabs' do
+    let(:project) { create(:empty_project) }
+    let(:user)    { create(:user) }
+
+    before do
+      allow(helper).to receive(:can?) { true }
+    end
+
+    subject do
+      helper.send(:get_project_nav_tabs, project, user)
+    end
+
+    context 'when builds feature is enabled' do
+      before do
+        allow(project).to receive(:builds_enabled?).and_return(true)
+      end
+
+      it "does include pipelines tab" do
+        is_expected.to include(:pipelines)
+      end
+    end
+
+    context 'when builds feature is disabled' do
+      before do
+        allow(project).to receive(:builds_enabled?).and_return(false)
+      end
+
+      it "do not include pipelines tab" do
+        is_expected.not_to include(:pipelines)
+      end
+    end
+  end
 end
diff --git a/spec/lib/gitlab/ci/status/external/common_spec.rb b/spec/lib/gitlab/ci/status/external/common_spec.rb
index 5a97d98b55f674db8cd23da51ddffcb4846fe4fd..e58f5d8d4df2a11720915f2bcda7a617816a6c69 100644
--- a/spec/lib/gitlab/ci/status/external/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/external/common_spec.rb
@@ -4,9 +4,10 @@ describe Gitlab::Ci::Status::External::Common do
   let(:user) { create(:user) }
   let(:project) { external_status.project }
   let(:external_target_url) { 'http://example.gitlab.com/status' }
+  let(:external_description) { 'my description' }
 
   let(:external_status) do
-    create(:generic_commit_status, target_url: external_target_url)
+    create(:generic_commit_status, target_url: external_target_url, description: external_description)
   end
 
   subject do
@@ -15,6 +16,12 @@ describe Gitlab::Ci::Status::External::Common do
       .extend(described_class)
   end
 
+  describe '#label' do
+    it 'returns description' do
+      expect(subject.label).to eq external_description
+    end
+  end
+
   describe '#has_action?' do
     it { is_expected.not_to have_action }
   end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 0d3af1f449999cedf0067f3a6cc57dd124d2fed0..848fd547e104860f50e04200fc47c55f51490877 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -139,6 +139,18 @@ describe ProjectPolicy, models: true do
           is_expected.not_to include(:read_build, :read_pipeline)
         end
       end
+
+      context 'when builds are disabled' do
+        before do
+          project.project_feature.update(
+            builds_access_level: ProjectFeature::DISABLED)
+        end
+
+        it do
+          is_expected.not_to include(:read_build)
+          is_expected.to include(:read_pipeline)
+        end
+      end
     end
 
     context 'reporter' do
diff --git a/spec/serializers/job_entity_spec.rb b/spec/serializers/job_entity_spec.rb
index 6a694576b4a1be931d0dbf3e43575868bcdfabf9..5ca7bf2fcaf14ba99afe952ba01bcceed05c8e7c 100644
--- a/spec/serializers/job_entity_spec.rb
+++ b/spec/serializers/job_entity_spec.rb
@@ -39,7 +39,7 @@ describe JobEntity do
     expect(subject[:status]).to include :icon, :favicon, :text, :label
   end
 
-  context 'when build is retryable' do
+  context 'when job is retryable' do
     before do
       job.update(status: :failed)
     end
@@ -49,7 +49,7 @@ describe JobEntity do
     end
   end
 
-  context 'when build is cancelable' do
+  context 'when job is cancelable' do
     before do
       job.update(status: :running)
     end
diff --git a/spec/serializers/pipeline_details_entity_spec.rb b/spec/serializers/pipeline_details_entity_spec.rb
index 03cc5ae9b63a7512a3ed1b54980d4e93ad350048..5cb9b9945b67d8fc2590edf23245ef6c92d67785 100644
--- a/spec/serializers/pipeline_details_entity_spec.rb
+++ b/spec/serializers/pipeline_details_entity_spec.rb
@@ -91,6 +91,20 @@ describe PipelineDetailsEntity do
       end
     end
 
+    context 'when pipeline has commit statuses' do
+      let(:pipeline) { create(:ci_empty_pipeline) }
+    
+      before do
+        create(:generic_commit_status, pipeline: pipeline)
+      end
+
+      it 'contains stages' do
+        expect(subject).to include(:details)
+        expect(subject[:details]).to include(:stages)
+        expect(subject[:details][:stages].first).to include(name: 'external')
+      end
+    end
+
     context 'when pipeline has YAML errors' do
       let(:pipeline) do
         create(:ci_pipeline, config: { rspec: { invalid: :value } })
diff --git a/spec/serializers/stage_entity_spec.rb b/spec/serializers/stage_entity_spec.rb
index 64b3217b8092b688e7294a80ef17fa9698e457c5..40e303f7b899323f795fe46e42cc23f19f496804 100644
--- a/spec/serializers/stage_entity_spec.rb
+++ b/spec/serializers/stage_entity_spec.rb
@@ -54,6 +54,17 @@ describe StageEntity do
       it 'exposes the group key' do
         expect(subject).to include :groups
       end
+
+      context 'and contains commit status' do
+        before do
+          create(:generic_commit_status, pipeline: pipeline, stage: 'test')
+        end
+
+        it 'contains commit status' do
+          groups = subject[:groups].map { |group| group[:name] }
+          expect(groups).to include('generic')
+        end
+      end
     end
   end
 end