EWS should have a way to retry a patch
https://bugs.webkit.org/show_bug.cgi?id=196599

Reviewed by Jonathan Bedard.

* BuildSlaveSupport/ews-app/ews/models/build.py:
(Build): Add the retried field to keep track of whether a build is requested to be retried or not.
(Build.set_retried): Method to set the retried field.
* BuildSlaveSupport/ews-app/ews/templates/statusbubble.html: Added the 'Retry failed builds' button.
* BuildSlaveSupport/ews-app/ews/views/retrypatch.py:
(RetryPatch.post): Added a check if the build is already retried. Also, set the retried flag appropriately.
* BuildSlaveSupport/ews-app/ews/views/statusbubble.py:
(StatusBubble._build_bubble): Updated the status-bubble to in-progress while waiting for build to be retried.
(StatusBubble._build_bubbles_for_patch): Display the retry button only if there are failed builds.
* BuildSlaveSupport/ews-app/ews/migrations/0002_build_retried.py: Added database migration.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251368 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Tools/BuildSlaveSupport/ews-app/ews/migrations/0002_build_retried.py b/Tools/BuildSlaveSupport/ews-app/ews/migrations/0002_build_retried.py
new file mode 100644
index 0000000..c049721
--- /dev/null
+++ b/Tools/BuildSlaveSupport/ews-app/ews/migrations/0002_build_retried.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.16 on 2019-10-18 18:46
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ews', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='build',
+            name='retried',
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/Tools/BuildSlaveSupport/ews-app/ews/models/build.py b/Tools/BuildSlaveSupport/ews-app/ews/models/build.py
index 890c423..6513f93 100644
--- a/Tools/BuildSlaveSupport/ews-app/ews/models/build.py
+++ b/Tools/BuildSlaveSupport/ews-app/ews/models/build.py
@@ -44,6 +44,7 @@
     state_string = models.TextField()
     started_at = models.IntegerField(null=True, blank=True)
     complete_at = models.IntegerField(null=True, blank=True)
+    retried = models.BooleanField(default=False)
     created = models.DateTimeField(auto_now_add=True)
     modified = models.DateTimeField(auto_now=True)
 
@@ -92,6 +93,15 @@
         return SUCCESS
 
     @classmethod
+    def set_retried(cls, uid, retried=True):
+        build = Build.get_existing_build(uid)
+        if not build:
+            return
+        build.retried = retried
+        build.save(update_fields=['retried', 'modified'])
+        _log.info('Updated build {} in database with retried={}'.format(uid, retried))
+
+    @classmethod
     def get_existing_build(cls, uid):
         try:
             return Build.objects.get(uid=uid)
diff --git a/Tools/BuildSlaveSupport/ews-app/ews/templates/statusbubble.html b/Tools/BuildSlaveSupport/ews-app/ews/templates/statusbubble.html
index a3240e4..357297a 100644
--- a/Tools/BuildSlaveSupport/ews-app/ews/templates/statusbubble.html
+++ b/Tools/BuildSlaveSupport/ews-app/ews/templates/statusbubble.html
@@ -48,6 +48,7 @@
     font-size: 9px;
 }
 form {
+    float: left;
     display: block;
 }
 </style>
@@ -100,6 +101,13 @@
   </form>
 {% endif %}
 
+{% if show_retry_button %}
+  <form name="retry" method="POST" action="/retry/">{% csrf_token %}
+    <input type="hidden" name="patch_id" value="{{ patch_id }}">
+    <input class="status" type="submit" value="&#8635; Retry failed builds">
+  </form>
+{% endif %}
+
 <script>
 // Convert from UTC dates to local.
 var bubbles = document.getElementsByClassName("status")
diff --git a/Tools/BuildSlaveSupport/ews-app/ews/views/retrypatch.py b/Tools/BuildSlaveSupport/ews-app/ews/views/retrypatch.py
index 118d379..ea886f3 100644
--- a/Tools/BuildSlaveSupport/ews-app/ews/views/retrypatch.py
+++ b/Tools/BuildSlaveSupport/ews-app/ews/views/retrypatch.py
@@ -25,11 +25,12 @@
 import logging
 
 from django.http import HttpResponse
-from django.shortcuts import render
+from django.shortcuts import redirect, render
 from django.views import View
 from django.views.decorators.clickjacking import xframe_options_exempt
 
 from ews.common.buildbot import Buildbot
+from ews.models.build import Build
 from ews.models.patch import Patch
 from ews.views.statusbubble import StatusBubble
 
@@ -55,12 +56,17 @@
 
         failed_to_retry_builds = []
         for build in builds_to_retry:
+            if build.retried:
+                _log.warn('Build {} for patch {} is already retried.'.format(build.uid, patch_id))
+                continue
+            Build.set_retried(build.uid, True)
             if not Buildbot.retry_build(build.builder_id, build.number):
                 failed_to_retry_builds.append(build)
+                Build.set_retried(build.uid, False)
 
         if len(failed_to_retry_builds) > 0:
             message = 'Failed to retry {} build(s) for patch {}.'.format(len(failed_to_retry_builds), patch_id)
             message += ' Please contact admin@webkit.org if the problem persist.'
             _log.warn(message)
             return HttpResponse(message)
-        return HttpResponse('Submitted {} build(s) to EWS for retry for patch {}.'.format(len(builds_to_retry), patch_id))
+        return redirect('/status-bubble/{}'.format(patch_id))
diff --git a/Tools/BuildSlaveSupport/ews-app/ews/views/statusbubble.py b/Tools/BuildSlaveSupport/ews-app/ews/views/statusbubble.py
index 16d234c..e7fd957 100644
--- a/Tools/BuildSlaveSupport/ews-app/ews/views/statusbubble.py
+++ b/Tools/BuildSlaveSupport/ews-app/ews/views/statusbubble.py
@@ -98,6 +98,9 @@
             else:
                 bubble['state'] = 'started'
             bubble['details_message'] = 'Build is in-progress. Recent messages:' + self._steps_messages_from_multiple_builds(builds)
+        elif build.retried:
+            bubble['state'] = 'started'
+            bubble['details_message'] = 'Waiting for available bot to retry the build.\n\nRecent messages:' + self._steps_messages_from_multiple_builds(builds)
         elif build.result == Buildbot.SUCCESS:
             if is_parent_build:
                 if patch.modified < (timezone.now() - datetime.timedelta(days=StatusBubble.DAYS_TO_CHECK)):
@@ -276,10 +279,11 @@
     def _build_bubbles_for_patch(self, patch, hide_icons=False):
         show_submit_to_ews = True
         failed_to_apply = False  # TODO: https://bugs.webkit.org/show_bug.cgi?id=194598
+        show_retry = False
         bubbles = []
 
         if not (patch and patch.sent_to_buildbot):
-            return (None, show_submit_to_ews, failed_to_apply)
+            return (None, show_submit_to_ews, failed_to_apply, show_retry)
 
         for queue in StatusBubble.ALL_QUEUES:
             if not self._should_show_bubble_for_queue(queue):
@@ -289,20 +293,23 @@
             if bubble:
                 show_submit_to_ews = False
                 bubbles.append(bubble)
+                if bubble['state'] in ('fail', 'error'):
+                    show_retry = True
 
-        return (bubbles, show_submit_to_ews, failed_to_apply)
+        return (bubbles, show_submit_to_ews, failed_to_apply, show_retry)
 
     @xframe_options_exempt
     def get(self, request, patch_id):
         hide_icons = request.GET.get('hide_icons', False)
         patch_id = int(patch_id)
         patch = Patch.get_patch(patch_id)
-        bubbles, show_submit_to_ews, show_failure_to_apply = self._build_bubbles_for_patch(patch, hide_icons)
+        bubbles, show_submit_to_ews, show_failure_to_apply, show_retry = self._build_bubbles_for_patch(patch, hide_icons)
 
         template_values = {
             'bubbles': bubbles,
             'patch_id': patch_id,
             'show_submit_to_ews': show_submit_to_ews,
             'show_failure_to_apply': show_failure_to_apply,
+            'show_retry_button': show_retry,
         }
         return render(request, 'statusbubble.html', template_values)
diff --git a/Tools/ChangeLog b/Tools/ChangeLog
index 2631e53..42393ff 100644
--- a/Tools/ChangeLog
+++ b/Tools/ChangeLog
@@ -1,3 +1,21 @@
+2019-10-18  Aakash Jain  <aakash_jain@apple.com>
+
+        EWS should have a way to retry a patch
+        https://bugs.webkit.org/show_bug.cgi?id=196599
+
+        Reviewed by Jonathan Bedard.
+
+        * BuildSlaveSupport/ews-app/ews/models/build.py:
+        (Build): Add the retried field to keep track of whether a build is requested to be retried or not.
+        (Build.set_retried): Method to set the retried field.
+        * BuildSlaveSupport/ews-app/ews/templates/statusbubble.html: Added the 'Retry failed builds' button.
+        * BuildSlaveSupport/ews-app/ews/views/retrypatch.py:
+        (RetryPatch.post): Added a check if the build is already retried. Also, set the retried flag appropriately.
+        * BuildSlaveSupport/ews-app/ews/views/statusbubble.py:
+        (StatusBubble._build_bubble): Updated the status-bubble to in-progress while waiting for build to be retried.
+        (StatusBubble._build_bubbles_for_patch): Display the retry button only if there are failed builds.
+        * BuildSlaveSupport/ews-app/ews/migrations/0002_build_retried.py: Added database migration.
+
 2019-10-21  Alicia Boya García  <aboya@igalia.com>
 
         [MSE][GStreamer] Revert WebKitMediaSrc rework temporarily