dist_templates_jobs_DATA = \
src/templates/jobs/abort.html \
- src/templates/jobs/log-stream.html
+ src/templates/jobs/log-stream.html \
+ src/templates/jobs/retry.html
templates_jobsdir = $(templatesdir)/jobs
return job
+ # Retry
+
+ def can_be_retried(self):
+ # The job must have failed
+ if not self.has_failed():
+ return False
+
+ # The job cannot have been superseeded by another job
+ if self.superseeded_by:
+ return False
+
+ return True
+
+ async def retry(self, user=None):
+ """
+ Creates a copy of a job
+ """
+ # Check
+ if not self.can_be_retried():
+ raise RuntimeError("Job %s cannot be retried" % self)
+
+ # Clone the job
+ job = self.clone()
+
+ # Schedule a dependency check
+ self.backend.run_task(self.depcheck)
+
# Log
def has_log(self):
{{ _("View Log") }}
</a>
- {% if job.is_running() %}
+ {% if job.can_be_retried() %}
+ <a class="button is-warning" href="/jobs/{{ job.uuid }}/retry">
+ {{ _("Retry") }}
+ </a>
+ {% elif job.is_running() %}
<a class="button is-dark" href="/jobs/{{ job.uuid }}/abort">
{{ _("Abort") }}
</a>
--- /dev/null
+{% extends "../base.html" %}
+
+{% block title %}{{ _("Retry Job %s") % job }}{% end block %}
+
+{% block container %}
+ <nav class="breadcrumb" aria-label="breadcrumbs">
+ <ul>
+ <li>
+ <a href="/builds">{{ _("Builds") }}</a>
+ </li>
+ <li>
+ <a href="/builds/{{ job.build.uuid }}">{{ job.build }}</a>
+ </li>
+ <li>
+ <a href="#" disabled>{{ job.arch }}</a>
+ </li>
+ <li class="is-active">
+ <a href="#" aria-current="page">{{ _("Retry") }}</a>
+ </li>
+ </ul>
+ </nav>
+
+ <div class="columns is-centered">
+ <div class="column is-6">
+ <form method="POST" action="">
+ <div class="box">
+ {% raw xsrf_form_html() %}
+
+ <p class="title is-4">{{ _("Retry Job") }}</p>
+ <p class="subtitle is-5">
+ {{ _("Do you want to retry building %s?") % job }}
+ </p>
+
+ <button class="button is-fullwidth is-warning">
+ {{ _("Retry") }}
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+{% end block %}
# Jobs
(r"/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/abort", jobs.AbortHandler),
(r"/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/log", jobs.LogHandler),
+ (r"/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/retry", jobs.RetryHandler),
(r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/buildroot", jobs.JobBuildrootHandler),
(r"/api/v1/jobs/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})",
jobs.APIv1DetailHandler),
self.redirect("/builds/%s" % job.build.uuid)
+class RetryHandler(base.BaseHandler):
+ @tornado.web.authenticated
+ def get(self, uuid):
+ job = self.backend.jobs.get_by_uuid(uuid)
+ if not job:
+ raise tornado.web.HTTPError(404, "Could not find job %s" % uuid)
+
+ # Check for permissions
+ if not job.has_perm(self.current_user):
+ raise tornado.web.HTTPError(403)
+
+ self.render("jobs/retry.html", job=job)
+
+ @tornado.web.authenticated
+ async def post(self, uuid):
+ job = self.backend.jobs.get_by_uuid(uuid)
+ if not job:
+ raise tornado.web.HTTPError(404, "Could not find job %s" % uuid)
+
+ with self.db.transaction():
+ await job.retry(self.current_user)
+
+ # Redirect back to the build page
+ self.redirect("/builds/%s" % job.build.uuid)
+
+
class ListModule(ui_modules.UIModule):
def render(self, jobs, show_arch_only=False, show_packages=False):
return self.render_string("jobs/modules/list.html", jobs=jobs,