diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5dbf66173de79101b39dee6034ec14db0ac88d68..7c9899334ad8c0944c38df42d66060f270ced29d 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -67,7 +67,7 @@ module Ci
           environment: build.environment,
           status_event: 'enqueue'
         )
-        MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
+        MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build.pipeline)
         build.pipeline.mark_as_processable_after_stage(build.stage_idx)
         new_build
       end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 663c5b1e2315a3def61cb9672786eb2cdae8f490..da0b9d83e1d5a51c6f2a4bc72665765fb34bc8f9 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -73,6 +73,14 @@ module Ci
       after_transition do |pipeline, transition|
         pipeline.execute_hooks unless transition.loopback?
       end
+
+      after_transition [:created, :pending, :running] => :success do |pipeline|
+        MergeRequests::MergeWhenBuildSucceedsService.new(pipeline.project, nil).trigger(pipeline)
+      end
+
+      after_transition any => :failed do |pipeline|
+        MergeRequests::AddTodoWhenBuildFailsService.new(pipeline.project, nil).execute(pipeline)
+      end
     end
 
     # ref can't be HEAD or SHA, can only be branch/tag name
@@ -251,9 +259,8 @@ module Ci
       Ci::ProcessPipelineService.new(project, user).execute(self)
     end
 
-    def build_updated
+    def update_status
       with_lock do
-        reload
         case latest_builds_status
         when 'pending' then enqueue
         when 'running' then run
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 736db1ab0f6da04e081bdd8b3324cd7cf98823c1..3d5e449f4037d4da3c8f160e4be216d6eb812168 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -74,10 +74,6 @@ class CommitStatus < ActiveRecord::Base
       true
     end
 
-    after_transition do |commit_status, transition|
-      commit_status.pipeline.try(:build_updated) unless transition.loopback?
-    end
-
     after_transition [:created, :pending, :running] => :success do |commit_status|
       MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.pipeline.project, nil).trigger(commit_status)
     end
@@ -85,6 +81,16 @@ class CommitStatus < ActiveRecord::Base
     after_transition any => :failed do |commit_status|
       MergeRequests::AddTodoWhenBuildFailsService.new(commit_status.pipeline.project, nil).execute(commit_status)
     end
+
+    after_transition do |commit_status, transition|
+      if commit_status.pipeline && !transition.loopback?
+        ProcessPipelineWorker.perform_async(
+          commit_status.pipeline.id,
+          process: HasStatus.COMPLETED_STATUSES.include?(commit_status.status))
+      end
+
+      true
+    end
   end
 
   delegate :sha, :short_sha, to: :pipeline
diff --git a/app/services/merge_requests/add_todo_when_build_fails_service.rb b/app/services/merge_requests/add_todo_when_build_fails_service.rb
index 566049525cb724ee4682780f7d9f5791071d0c00..45c737898861e6e8b464231cca68bcf261c1c2fe 100644
--- a/app/services/merge_requests/add_todo_when_build_fails_service.rb
+++ b/app/services/merge_requests/add_todo_when_build_fails_service.rb
@@ -1,15 +1,15 @@
 module MergeRequests
   class AddTodoWhenBuildFailsService < MergeRequests::BaseService
     # Adds a todo to the parent merge_request when a CI build fails
-    def execute(commit_status)
-      each_merge_request(commit_status) do |merge_request|
+    def execute(pipeline)
+      each_merge_request(pipeline) do |merge_request|
         todo_service.merge_request_build_failed(merge_request)
       end
     end
 
     # Closes any pending build failed todos for the parent MRs when a build is retried
-    def close(commit_status)
-      each_merge_request(commit_status) do |merge_request|
+    def close(pipeline)
+      each_merge_request(pipeline) do |merge_request|
         todo_service.merge_request_build_retried(merge_request)
       end
     end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index d0d155b7ee1e8dd4b0752c8c5a4fe8099f9d3fcd..95b4f0ff7331e4446989c8cf045488c90a5ee9ae 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -42,11 +42,11 @@ module MergeRequests
       super(:merge_request)
     end
 
-    def merge_request_from(commit_status)
-      branches = commit_status.ref
+    def merge_request_from(pipeline)
+      branches = pipeline.ref
 
       # This is for ref-less builds
-      branches ||= @project.repository.branch_names_contains(commit_status.sha)
+      branches ||= @project.repository.branch_names_contains(pipeline.sha)
 
       return [] if branches.blank?
 
@@ -56,14 +56,11 @@ module MergeRequests
       merge_requests.uniq.select(&:source_project)
     end
 
-    def each_merge_request(commit_status)
+    def each_merge_request(pipeline)
       merge_request_from(commit_status).each do |merge_request|
-        pipeline = merge_request.pipeline
+        next unless pipeline == merge_request.pipeline
 
-        next unless pipeline
-        next unless pipeline.sha == commit_status.sha
-
-        yield merge_request, pipeline
+        yield merge_request
       end
     end
   end
diff --git a/app/services/merge_requests/merge_when_build_succeeds_service.rb b/app/services/merge_requests/merge_when_build_succeeds_service.rb
index 4ad5fb083114400d4b4bd8aef1a573e9cd71227f..60d6085f9eb6472229414cf667006d6d1d57b2c8 100644
--- a/app/services/merge_requests/merge_when_build_succeeds_service.rb
+++ b/app/services/merge_requests/merge_when_build_succeeds_service.rb
@@ -19,11 +19,12 @@ module MergeRequests
     end
 
     # Triggers the automatic merge of merge_request once the build succeeds
-    def trigger(commit_status)
-      each_merge_request(commit_status) do |merge_request, pipeline|
+    def trigger(pipeline)
+      return unless pipeline.success?
+
+      each_merge_request(pipeline) do |merge_request|
         next unless merge_request.merge_when_build_succeeds?
         next unless merge_request.mergeable?
-        next unless pipeline.success?
 
         MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
       end
diff --git a/app/workers/process_pipeline_worker.rb b/app/workers/process_pipeline_worker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fb59a1efb7a251cd83b1ac1c415b6f0eceb8d814
--- /dev/null
+++ b/app/workers/process_pipeline_worker.rb
@@ -0,0 +1,17 @@
+class ProcessPipelineWorker
+  include Sidekiq::Worker
+
+  sidekiq_options queue: :default
+
+  def perform(pipeline_id, params)
+    begin
+      pipeline = Ci::Pipeline.find(pipeline_id)
+    rescue ActiveRecord::RecordNotFound
+      return
+    end
+
+    pipeline.process! if params[:process]
+
+    pipeline.update_status
+  end
+end