Merge "Comment on PRs if a remote call to merge a change failed" into feature/zuulv3
diff --git a/tests/fixtures/layouts/merging-github.yaml b/tests/fixtures/layouts/merging-github.yaml
index 4e13063..9f43f75 100644
--- a/tests/fixtures/layouts/merging-github.yaml
+++ b/tests/fixtures/layouts/merging-github.yaml
@@ -2,6 +2,7 @@
     name: merge
     description: Pipeline for merging the pull request
     manager: independent
+    merge-failure-message: 'Merge failed'
     trigger:
       github:
         - event: pull_request
diff --git a/tests/unit/test_github_driver.py b/tests/unit/test_github_driver.py
index f287790..c8e7341 100644
--- a/tests/unit/test_github_driver.py
+++ b/tests/unit/test_github_driver.py
@@ -335,6 +335,8 @@
         self.fake_github.emitEvent(D.getCommentAddedEvent('merge me'))
         self.waitUntilSettled()
         self.assertFalse(D.is_merged)
+        self.assertEqual(len(D.comments), 1)
+        self.assertEqual(D.comments[0], 'Merge failed')
 
     @simple_layout('layouts/dependent-github.yaml', driver='github')
     def test_parallel_changes(self):
diff --git a/zuul/driver/github/githubreporter.py b/zuul/driver/github/githubreporter.py
index ffec26a..9975cbc 100644
--- a/zuul/driver/github/githubreporter.py
+++ b/zuul/driver/github/githubreporter.py
@@ -49,11 +49,14 @@
         if (self._merge and
             hasattr(item.change, 'number')):
             self.mergePull(item)
+            if not item.change.is_merged:
+                msg = self._formatItemReportMergeFailure(pipeline, item)
+                self.addPullComment(pipeline, item, msg)
         if self._labels or self._unlabels:
             self.setLabels(item)
 
-    def addPullComment(self, pipeline, item):
-        message = self._formatItemReport(pipeline, item)
+    def addPullComment(self, pipeline, item, comment=None):
+        message = comment or self._formatItemReport(pipeline, item)
         project = item.change.project.name
         pr_number = item.change.number
         self.log.debug(
@@ -92,13 +95,21 @@
         self.log.debug('Reporting change %s, params %s, merging via API' %
                        (item.change, self.config))
         message = self._formatMergeMessage(item.change)
-        try:
-            self.connection.mergePull(project, pr_number, message, sha)
-        except MergeFailure:
-            time.sleep(2)
-            self.log.debug('Trying to merge change %s again...' % item.change)
-            self.connection.mergePull(project, pr_number, message, sha)
-        item.change.is_merged = True
+
+        for i in [1, 2]:
+            try:
+                self.connection.mergePull(project, pr_number, message, sha)
+                item.change.is_merged = True
+                return
+            except MergeFailure:
+                self.log.debug(
+                    'Merge attempt of change %s  %s/2 failed.' %
+                    (i, item.change))
+                if i == 1:
+                    time.sleep(2)
+        self.log.debug(
+            'Merge of change %s failed after 2 attempts, giving up' %
+            item.change)
 
     def setLabels(self, item):
         project = item.change.project.name