templates_distros_modulesdir = $(templates_distrosdir)/modules
dist_templates_monitorings_DATA = \
+ src/templates/monitorings/delete.html \
+ src/templates/monitorings/edit.html \
src/templates/monitorings/show.html
templates_monitoringsdir = $(templatesdir)/monitorings
return body
- def _get_monitoring(self, query, *args):
- res = self.db.get(query, *args)
+ def _get_monitoring(self, query, *args, **kwargs):
+ return self.db.fetch_one(Monitoring, query, *args, **kwargs)
- if res:
- return Monitoring(self.backend, res.id, res)
-
- def _get_monitorings(self, query, *args):
- res = self.db.query(query, *args)
-
- for row in res:
- yield Monitoring(self.backend, row.id, row)
+ def _get_monitorings(self, query, *args, **kwargs):
+ return self.db.fetch_many(Monitoring, query, *args, **kwargs)
def get_by_id(self, id):
return self._get_monitoring("""
distro_id = %s
AND
name = %s
- """, distro, name,
+ """, distro, name, distro=distro,
)
async def create(self, distro, name, created_by, project_id,
- follow="mainline", create_builds=True):
+ follow="mainline", create_builds=True, check=True):
monitoring = self._get_monitoring("""
INSERT INTO
release_monitorings
)
RETURNING
*
- """, distro, name, created_by, project_id, follow,
+ """, distro, name, created_by, project_id, follow, create_builds,
+ distro=distro,
)
- # Schedule the first check in the background
- self.backend.run_task(monitoring.check)
+ # Perform the first check immediately
+ if check:
+ await monitoring.check()
return monitoring
def follow(self):
return self.data.follow
- @property
- def create_builds(self):
+ # Create Builds
+
+ def get_create_builds(self):
return self.data.create_builds
+ def set_create_builds(self, value):
+ self._set_attribute("create_builds", value)
+
+ create_builds = property(get_create_builds, set_create_builds)
+
# Permissions
def has_perm(self, user=None):
# Users must be admins
return user.is_admin()
+ # Delete
+
+ async def delete(self, user=None):
+ # Mark as deleted
+ self._set_attribute_now("deleted_at")
+ if user:
+ self._set_attribute("deleted_by", user)
+
+ # Delete all releases
+ async with asyncio.TaskGroup() as tasks:
+ for release in self.releases:
+ tasks.create_task(release.delete())
+
# Check
async def check(self):
Returns True if a build with this version already exists
"""
# XXX needs to check if we already have a newer version
+ # XXX packages don't have version separately
+ # XXX How do we get this from EVR?
- return version in [build.package.version for build in self.builds]
+ #return version in [build.pkg.version for build in self.builds]
@property
def builds(self):
def created_at(self):
return self.data.created_at
+ # Delete
+
+ async def delete(self, user=None):
+ """
+ Deletes this release
+ """
+ async with asyncio.TaskGroup() as tasks:
+ # Delete the build
+ if self.build:
+ tasks.create_task(self.build.delete(user=user))
+
+ # Close the bug
+ tasks.create_task(
+ self._close_bug(
+ resolution="WONTFIX",
+ comment="Release Monitoring for this package has been terminated",
+ ),
+ )
+
# Bug
async def _create_bug(self):
return bug
+ async def _close_bug(self, *args, **kwargs):
+ # Fetch the bug
+ bug = await self.get_bug()
+
+ if bug and not bug.is_closed():
+ await bug.close(*args, **kwargs)
+
@property
def bug_id(self):
return self.data.bug_id
+ async def get_bug(self):
+ """
+ Fetches the bug from Bugzilla
+ """
+ if self.bug_id:
+ return await self.backend.bugzilla.get_bug(self.bug_id)
+
# Build
def get_build(self):
--- /dev/null
+{% extends "../modal.html" %}
+
+{% block title %}{{ _("Delete Release Monitoring") }} - {{ monitoring }}{% end block %}
+
+{% block breadcrumbs %}
+ <nav class="breadcrumb" aria-label="breadcrumbs">
+ <ul>
+ <li>
+ <a href="/distros">{{ _("Distributions") }}</a>
+ </li>
+ <li>
+ <a href="/distros/{{ monitoring.distro.slug }}">{{ monitoring.distro }}</a>
+ </li>
+ <li>
+ <a href="#" disabled>{{ _("Monitorings") }}</a>
+ </li>
+ <li class="is-active">
+ <a href="/distros/{{ monitoring.distro.slug }}/monitorings/{{ monitoring.name }}"
+ aria-current="page">{{ monitoring.name }}</a>
+ </li>
+ </ul>
+ </nav>
+{% end block %}
+
+{% block modal_title %}
+ <h4 class="title is-4">{{ _("Delete Release Monitoring") }}</h4>
+ <h6 class="subtitle is-6">{{ monitoring }}</h6>
+{% end block %}
+
+{% block modal %}
+ <form method="POST" action="">
+ {% raw xsrf_form_html() %}
+
+ <div class="content">
+ <p>
+ {{ _("Are you sure you want to delete %s?") % monitoring }}
+ </p>
+ </div>
+
+ {# Submit! #}
+ <div class="field">
+ <button type="submit" class="button is-danger is-fullwidth">
+ {{ _("Delete") }}
+ </button>
+ </div>
+ </form>
+{% end block %}
--- /dev/null
+{% extends "../modal.html" %}
+
+{% block title %}
+ {% if monitoring %}
+ {{ _("Edit Release Monitoring") }} - {{ monitoring }}
+ {% else %}
+ {{ _("Create Release Monitoring") }}
+ {% end %}
+{% end block %}
+
+{% block breadcrumbs %}
+ <nav class="breadcrumb" aria-label="breadcrumbs">
+ <ul>
+ <li>
+ <a href="/distros">{{ _("Distributions") }}</a>
+ </li>
+ <li>
+ <a href="/distros/{{ distro.slug }}">{{ distro }}</a>
+ </li>
+ <li>
+ <a href="#" disabled>{{ _("Monitorings") }}</a>
+ </li>
+ <li class="is-active">
+ <a href="/distros/{{ distro.slug }}/monitorings/{{ name }}"
+ aria-current="page">{{ name }}</a>
+ </li>
+ {% if monitoring %}
+ <li class="is-active">
+ <a href="#" aria-current="page">{{ _("Edit") }}</a>
+ </li>
+ {% else %}
+ <li class="is-active">
+ <a href="#" aria-current="page">{{ _("Create") }}</a>
+ </li>
+ {% end %}
+ </ul>
+ </nav>
+{% end block %}
+
+{% block modal_title %}
+ {% if monitoring %}
+ <h4 class="title is-4">{{ _("Edit Release Monitoring") }}</h4>
+ <h6 class="subtitle is-6">{{ monitoring }}</h6>
+ {% else %}
+ <h4 class="title is-4">{{ _("Create A New Release Monitoring") }}</h4>
+ {% end %}
+{% end block %}
+
+{% block modal %}
+ <form method="POST" action="">
+ {% raw xsrf_form_html() %}
+
+ {# Project & Follow can only be set once #}
+ {% if not monitoring %}
+ {# Project #}
+ <div class="field">
+ <label class="label">{{ _("Project") }}</label>
+ <div class="control">
+ <div class="select">
+ <select name="project_id" required>
+ {% for project in projects %}
+ <option value="{{ project.id }}">
+ {{ project.name }}
+ - {{ project.homepage or project.ecosystem }}
+ - {{ project.version }}
+ </option>
+ {% end %}
+ </select>
+ </div>
+ </div>
+ <p class="help">
+ {{ _("Select an upstream project that matches this package.") }}
+ </p>
+ </div>
+
+ {% set follows = {
+ "mainline" : _("Mainline - Follow the latest releases"),
+ } %}
+
+ {# Follow #}
+ <div class="field">
+ <label class="label">{{ _("Which Release Path To Follow?") }}</label>
+ <div class="control">
+ <div class="select">
+ <select name="follow" required>
+ {% for follow in follows %}
+ <option value="{{ follow }}"
+ {% if monitoring and monitoring.follow == follow %}selected{% end %}>
+ {{ follows[follow] }}
+ </option>
+ {% end %}
+ </select>
+ </div>
+ </div>
+ <p class="help">
+ {{ _("Select which releases to follow.") }}
+ </p>
+ </div>
+ {% end %}
+
+ {# Create Builds? #}
+ <div class="field">
+ <label class="label">{{ _("Builds") }}</label>
+ <div class="control">
+ <label class="checkbox">
+ <input type="checkbox" name="create_builds"
+ {% if (monitoring and monitoring.create_builds) or not monitoring %}checked{% end %}>
+ {{ _("Create Builds For Each New Release") }}
+ </label>
+ </div>
+ </div>
+
+ {# Submit! #}
+ <div class="field">
+ {% if monitoring %}
+ <button type="submit" class="button is-warning is-fullwidth">
+ {{ _("Save") }}
+ </button>
+ {% else %}
+ <button type="submit" class="button is-success is-fullwidth">
+ {{ _("Create") }}
+ </button>
+ {% end %}
+ </div>
+ </form>
+{% end block %}
<div>
<p class="heading">{{ _("Last Check") }}</p>
<p>
- {{ locale.format_date(monitoring.last_check_at, shorter=True) }}
+ {% if monitoring.last_check_at %}
+ {{ locale.format_date(monitoring.last_check_at, shorter=True) }}
+ {% else %}
+ {{ _("N/A") }}
+ {% end %}
</p>
</div>
</div>
</a>
</div>
{% else %}
- <a class="button is-success" href="{{ monitoring.url }}/create">
+ <a class="button is-success" href="/distros/{{ distro.slug }}/monitorings/{{ package.name }}/create">
{{ _("Enable Release Monitoring") }}
</a>
{% end %}
# Distro Monitorings
(r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)", monitorings.ShowHandler),
(r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/check", monitorings.CheckHandler),
+ (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/create", monitorings.CreateHandler),
+ (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/delete", monitorings.DeleteHandler),
+ (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/edit", monitorings.EditHandler),
# Mirrors
(r"/mirrors", mirrors.IndexHandler),
self.render("monitorings/show.html", monitoring=monitoring)
-class CheckHandler(base.BaseHandler):
+class CreateHandler(base.BaseHandler):
+ @tornado.web.authenticated
+ async def get(self, slug, name):
+ # Fetch the distribution
+ distro = self.backend.distros.get_by_slug(slug)
+ if not distro:
+ raise tornado.web.HTTPError(404, "Could not find distro %s" % slug)
+
+ # Fetch the monitoring
+ monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name)
+ if monitoring:
+ raise tornado.web.HTTPError(400, "Monitoring for %s in %s already exists" % (name, distro))
+
+ # Check permissions
+ if not self.current_user.is_admin():
+ raise tornado.web.HTTPError(403)
+
+ # Fetch the search query
+ q = self.get_argument("q", name)
+
+ # Search for projects
+ projects = await self.backend.monitorings.search(q)
+
+ self.render("monitorings/edit.html", monitoring=None, distro=distro, name=name,
+ projects=projects)
+
+ @tornado.web.authenticated
+ async def post(self, slug, name):
+ # Fetch the distribution
+ distro = self.backend.distros.get_by_slug(slug)
+ if not distro:
+ raise tornado.web.HTTPError(404, "Could not find distro %s" % slug)
+
+ # Fetch the monitoring
+ monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name)
+ if monitoring:
+ raise tornado.web.HTTPError(400, "Monitoring for %s in %s already exists" % (name, distro))
+
+ # Check permissions
+ if not self.current_user.is_admin():
+ raise tornado.web.HTTPError(403)
+
+ # Collect parameters
+ project_id = self.get_argument_int("project_id")
+ follow = self.get_argument("follow")
+ create_builds = self.get_argument_bool("create_builds")
+
+ with self.db.transaction():
+ try:
+ # Create a new monitoring
+ monitoring = await self.backend.monitorings.create(distro, name,
+ created_by=self.current_user, project_id=project_id,
+ follow=follow, create_builds=create_builds,
+ )
+
+ except ValueError as e:
+ raise tornado.web.HTTPError(400, "%s" % e) from e
+
+ # Redirect to the newly created monitoring
+ self.redirect(monitoring.url)
+
+
+class EditHandler(base.BaseHandler):
+ @tornado.web.authenticated
+ def get(self, slug, name):
+ # Fetch the distribution
+ distro = self.backend.distros.get_by_slug(slug)
+ if not distro:
+ raise tornado.web.HTTPError(404, "Could not find distro %s" % slug)
+
+ # Fetch the monitoring
+ monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name)
+ if not monitoring:
+ raise tornado.web.HTTPError(404, "Could not find monitoring for %s in %s" % (name, distro))
+
+ # Check permissions
+ if not monitoring.has_perm(self.current_user):
+ raise tornado.web.HTTPError(403)
+
+ self.render("monitorings/edit.html", monitoring=monitoring, distro=distro, name=name)
+
+ @tornado.web.authenticated
+ def post(self, slug, name):
+ # Fetch the distribution
+ distro = self.backend.distros.get_by_slug(slug)
+ if not distro:
+ raise tornado.web.HTTPError(404, "Could not find distro %s" % slug)
+
+ # Fetch the monitoring
+ monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name)
+ if not monitoring:
+ raise tornado.web.HTTPError(404, "Could not find monitoring for %s in %s" % (name, distro))
+
+ # Check permissions
+ if not monitoring.has_perm(self.current_user):
+ raise tornado.web.HTTPError(403)
+
+ with self.db.transaction():
+ monitoring.create_builds = self.get_argument_bool("create_builds")
+
+ self.redirect(monitoring.url)
+
+
+class DeleteHandler(base.BaseHandler):
+ @tornado.web.authenticated
+ def get(self, slug, name):
+ # Fetch the distribution
+ distro = self.backend.distros.get_by_slug(slug)
+ if not distro:
+ raise tornado.web.HTTPError(404, "Could not find distro %s" % slug)
+
+ # Fetch the monitoring
+ monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name)
+ if not monitoring:
+ raise tornado.web.HTTPError(404, "Could not find monitoring for %s in %s" % (name, distro))
+
+ # Check permissions
+ if not monitoring.has_perm(self.current_user):
+ raise tornado.web.HTTPError(403)
+
+ self.render("monitorings/delete.html", monitoring=monitoring)
+
@tornado.web.authenticated
async def post(self, slug, name):
# Fetch the distribution
if not monitoring.has_perm(self.current_user):
raise tornado.web.HTTPError(403)
- # Perform check
with self.db.transaction():
- await monitoring.check()
+ await monitoring.delete(self.current_user)
+
+ self.redirect("/packages/%s" % monitoring.name)
+
+
+class CheckHandler(base.BaseHandler):
+ @tornado.web.authenticated
+ async def post(self, slug, name):
+ # Fetch the distribution
+ distro = self.backend.distros.get_by_slug(slug)
+ if not distro:
+ raise tornado.web.HTTPError(404, "Could not find distro %s" % slug)
+
+ # Fetch the monitoring
+ monitoring = self.backend.monitorings.get_by_distro_and_name(distro, name)
+ if not monitoring:
+ raise tornado.web.HTTPError(404, "Could not find monitoring for %s in %s" % (name, distro))
+
+ # Check permissions
+ if not monitoring.has_perm(self.current_user):
+ raise tornado.web.HTTPError(403)
+
+ # Perform check (it is already starting its own transaction)
+ await monitoring.check()
# Redirect back
self.redirect(monitoring.url)