async def create(self, distro, name, created_by, project_id,
follow="stable", create_builds=True):
- monitoring = await self._get_monitoring("""
- INSERT INTO
- release_monitorings
- (
- distro_id,
- name,
- created_by,
- project_id,
- follow,
- create_builds
- )
- VALUES(
- %s, %s, %s, %s, %s, %s
- )
- RETURNING
- *
- """, distro, name, created_by, project_id, follow, create_builds,
-
- # Populate cache
- distro=distro,
+ """
+ Creates a new monitoring
+ """
+ return await self.db.insert(
+ Monitoring,
+ distro = distro,
+ name = name,
+ created_by = created_by,
+ project_id = project_id,
+ follow = follow,
+ create_builds = create_builds,
)
- return monitoring
-
async def search(self, name):
"""
Returns a bunch of packages that match the given name
)
# Return all packages
- return [database.Row(item) for item in response.get("items")]
+ return [item for item in response.get("items")]
async def check(self, limit=None):
"""
Perform checks on monitorings
"""
# Fetch all monitorings that were never checked or checked longer than 24 hours ago
- monitorings = self._get_monitorings("""
- SELECT
- *
- FROM
- release_monitorings
- WHERE
- deleted_at IS NULL
- AND
- (
- last_check_at IS NULL
- OR
- last_check_at <= CURRENT_TIMESTAMP - INTERVAL '24 hours'
- )
- ORDER BY
- last_check_at ASC NULLS FIRST
- LIMIT
- %s
- """, limit,
+ stmt = (
+ sqlalchemy
+ .select(
+ Monitoring,
+ )
+ .where(
+ Monitoring.deleted_at == None,
+
+ sqlalchemy.or_(
+ Monitoring.last_check_at == None,
+ Monitoring.last_check_at ==
+ sqlalchemy.func.current_timestamp() - datetime.timedelta(days=24),
+ ),
+ )
+ .order_by(
+ Monitoring.last_check_at.asc(),
+ )
)
- async for monitoring in monitorings:
+ # Run checks for all of them
+ async for monitoring in self.db.fetch(stmt):
await monitoring.check()
+ # Commit after each check
+ await self.db.commit()
+
class Monitoring(database.Base, database.BackendMixin, database.SoftDeleteMixin):
__tablename__ = "release_monitorings"
# Distro
- distro = sqlalchemy.orm.relationship("Distro", lazy="joined")
+ distro = sqlalchemy.orm.relationship("Distro", lazy="joined", innerjoin=True)
# Name
# Created By
created_by = sqlalchemy.orm.relationship(
- "User", foreign_keys=[created_by_id], lazy="joined",
+ "User", foreign_keys=[created_by_id], lazy="joined", innerjoin=True,
)
# Deleted By ID
# Deleted By
deleted_by = sqlalchemy.orm.relationship(
- "User", foreign_keys=[deleted_by_id], lazy="joined",
+ "User", foreign_keys=[deleted_by_id], lazy="selectin",
)
# Project ID
# Delete
- async def delete(self, user=None):
- # Mark as deleted
- await self._set_attribute_now("deleted_at")
- if user:
- await self._set_attribute("deleted_by", user)
+ async def delete(self, *args, **kwargs):
+ """
+ Deletes this monitoring object
+ """
+ await super().delete(*args, **kwargs)
# Delete all releases
- async with asyncio.TaskGroup() as tasks:
- for release in self.releases:
- tasks.create_task(release.delete())
+ async for release in self.get_releases():
+ await release.delete()
# Check
try:
if self.follow == "latest":
- release = await self._follow_latest(versions)
+ release = await self._follow_latest(versions, build=latest_build)
elif self.follow == "stable":
- release = await self._follow_stable(versions)
+ release = await self._follow_stable(versions, build=latest_build)
elif self.follow == "current-branch":
- release = await self._follow_current_branch(versions, latest_build)
+ release = await self._follow_current_branch(versions, build=latest_build)
else:
raise ValueError("Cannot handle follow: %s" % self.follow)
"""
# Wait until we are allowed to send an API request
async with ratelimiter:
- response = await self.backend.monitorings._request(
+ return await self.backend.monitorings._request(
"GET", "/api/v2/versions/", {
"project_id" : self.project_id,
},
)
- # Parse the response as JSON and return it
- return database.Row(response)
-
async def _follow_stable(self, versions, *, build):
"""
This will follow "stable" i.e. the latest stable version
"""
- for version in versions.stable_versions:
+ for version in versions.get("stable_versions", []):
return await self.create_release(version, build=build)
async def _follow_latest(self, versions, * build):
if not build:
return
+ # Fetch all stable versions
+ stable_versions = versions.get("stable_versions", [])
+
# Find the next version
- next_version = self._find_next_version(
- latest_build.pkg.evr, versions.stable_versions)
+ next_version = self._find_next_version(latest_build.pkg.evr, stable_versions)
# Create a new release with the next version
if next_version:
"""
Returns True if this version already exists
"""
- return version in [release.version async for release in self.releases]
+ return version in [release.version async for release in self.get_releases()]
async def create_release(self, version, *, build):
"""
raise ReleaseExistsError(version)
# Raise an error if we already have a newer build
- elif self._build_exists(version):
+ elif await self._build_exists(version):
raise BuildExistsError(version)
log.info("%s: Creating new release %s" % (self, version))
# Builds
- def _build_exists(self, version):
+ async def _build_exists(self, version):
"""
Returns True if a build with this version already exists
"""
+ latest_build = await self.get_latest_build()
+
# If there is no build to check against we return False
- if not self.latest_build:
+ if not latest_build:
return False
# Compare the versions
- if pakfire.version_compare(self.latest_build.pkg.evr, version) > 0:
+ if pakfire.version_compare(latest_build.pkg.evr, version) > 0:
return True
return False
-{% extends "../modal.html" %}
+{% extends "modal.html" %}
-{% block title %}{{ _("Delete Release Monitoring") }} - {{ monitoring }}{% end block %}
+{% block title %}{{ _("Delete Release Monitoring") }} - {{ monitoring }}{% endblock %}
{% block breadcrumbs %}
<nav class="breadcrumb" aria-label="breadcrumbs">
</li>
</ul>
</nav>
-{% end block %}
+{% endblock %}
{% block modal_title %}
<h4 class="title is-4">{{ _("Delete Release Monitoring") }}</h4>
<h6 class="subtitle is-6">{{ monitoring }}</h6>
-{% end block %}
+{% endblock %}
{% block modal %}
<form method="POST" action="">
- {% raw xsrf_form_html() %}
+ {{ xsrf_form_html() | safe }}
<div class="content">
<p>
</button>
</div>
</form>
-{% end block %}
+{% endblock %}
-{% extends "../modal.html" %}
+{% extends "modal.html" %}
{% block title %}
{% if monitoring %}
{{ _("Edit Release Monitoring") }} - {{ monitoring }}
{% else %}
{{ _("Create Release Monitoring") }}
- {% end %}
-{% end block %}
+ {% endif %}
+{% endblock %}
{% block breadcrumbs %}
<nav class="breadcrumb" aria-label="breadcrumbs">
<li class="is-active">
<a href="#" aria-current="page">{{ _("Create") }}</a>
</li>
- {% end %}
+ {% endif %}
</ul>
</nav>
-{% end block %}
+{% endblock %}
{% block modal_title %}
{% if monitoring %}
<h6 class="subtitle is-6">{{ monitoring }}</h6>
{% else %}
<h4 class="title is-4">{{ _("Create A New Release Monitoring") }}</h4>
- {% end %}
-{% end block %}
+ {% endif %}
+{% endblock %}
{% block modal %}
<form method="POST" action="">
- {% raw xsrf_form_html() %}
+ {{ xsrf_form_html() | safe }}
{# 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">
+ <div class="select is-fullwidth">
<select name="project_id" required>
{% for project in projects %}
<option value="{{ project.id }}">
{{ project.name }}
- - {{ project.homepage or project.ecosystem }}
+ -
+ {{ project.homepage or project.ecosystem }}
</option>
- {% end %}
+ {% endfor %}
</select>
</div>
</div>
+
<p class="help">
{{ _("Select an upstream project that matches this package.") }}
</p>
{# Follow #}
<div class="field">
- <label class="label">{{ _("Which Release Path To Follow?") }}</label>
+ <label class="label">
+ {{ _("Which Release Path To Follow?") }}
+ </label>
+
<div class="control">
- <div class="select">
+ <div class="select is-fullwidth">
<select name="follow" required>
{% for follow in follows %}
<option value="{{ follow }}"
- {% if monitoring and monitoring.follow == follow %}selected{% end %}>
+ {% if monitoring and monitoring.follow == follow %}selected{% endif %}>
{{ follows[follow] }}
</option>
- {% end %}
+ {% endfor %}
</select>
</div>
</div>
+
<p class="help">
{{ _("Select which releases to follow.") }}
</p>
</div>
- {% end %}
+ {% endif %}
{# Create Builds? #}
<div class="field">
- <label class="label">{{ _("Builds") }}</label>
+ <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 %}>
+ {% if (monitoring and monitoring.create_builds) or not monitoring %}checked{% endif %}>
{{ _("Create Builds For Each New Release") }}
</label>
</div>
<button type="submit" class="button is-success is-fullwidth">
{{ _("Create") }}
</button>
- {% end %}
+ {% endif %}
</div>
</form>
-{% end block %}
+{% endblock %}
</div>
{% endif %}
- {% if monitoring.latest_build %}
+ {% set latest_build = monitoring.get_latest_build() %}
+ {% if latest_build %}
<div class="level-item has-text-centered">
<div>
<p class="heading">{{ _("Current Release") }}</p>
<p>
- <a href="/builds/{{ monitoring.latest_build.uuid }}">
- {{ monitoring.latest_build.pkg.evr }}
+ <a href="/builds/{{ latest_build.uuid }}">
+ {{ latest_build.pkg.evr }}
</a>
</p>
</div>
@base.authenticated
async def get(self, slug, name):
# Fetch the distribution
- distro = self.backend.distros.get_by_slug(slug)
+ distro = await 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)
+ monitoring = await 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))
# Search for projects
projects = await self.backend.monitorings.search(q)
- self.render("monitorings/edit.html", monitoring=None, distro=distro, name=name,
- projects=projects)
+ await self.render("monitorings/edit.html", monitoring=None,
+ distro=distro, name=name, projects=projects)
@base.authenticated
async def post(self, slug, name):
# Fetch the distribution
- distro = self.backend.distros.get_by_slug(slug)
+ distro = await 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)
+ monitoring = await 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))
follow = self.get_argument("follow")
create_builds = self.get_argument_bool("create_builds")
- with self.db.transaction():
+ async with await 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,
+ monitoring = await self.backend.monitorings.create(
+ distro = distro,
+ name = 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
# Perform check immediately
- with self.db.transaction():
+ async with await self.db.transaction():
await monitoring.check()
# Redirect to the newly created monitoring
class EditHandler(base.BaseHandler):
@base.authenticated
- def get(self, slug, name):
+ async def get(self, slug, name):
# Fetch the distribution
- distro = self.backend.distros.get_by_slug(slug)
+ distro = await 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)
+ monitoring = await 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))
if not monitoring.has_perm(self.current_user):
raise tornado.web.HTTPError(403)
- self.render("monitorings/edit.html", monitoring=monitoring, distro=distro, name=name)
+ await self.render("monitorings/edit.html", monitoring=monitoring, distro=distro, name=name)
@base.authenticated
- def post(self, slug, name):
+ async def post(self, slug, name):
# Fetch the distribution
- distro = self.backend.distros.get_by_slug(slug)
+ distro = await 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)
+ monitoring = await 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))
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")
+ # Store values
+ monitoring.create_builds = self.get_argument_bool("create_builds")
self.redirect(monitoring.url)
class DeleteHandler(base.BaseHandler):
@base.authenticated
- def get(self, slug, name):
+ async def get(self, slug, name):
# Fetch the distribution
- distro = self.backend.distros.get_by_slug(slug)
+ distro = await 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)
+ monitoring = await 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))
if not monitoring.has_perm(self.current_user):
raise tornado.web.HTTPError(403)
- self.render("monitorings/delete.html", monitoring=monitoring)
+ await self.render("monitorings/delete.html", monitoring=monitoring)
@base.authenticated
async def post(self, slug, name):
# Fetch the distribution
- distro = self.backend.distros.get_by_slug(slug)
+ distro = await 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)
+ monitoring = await 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))
if not monitoring.has_perm(self.current_user):
raise tornado.web.HTTPError(403)
- with self.db.transaction():
- await monitoring.delete(self.current_user)
+ # Delete the monitoring
+ await monitoring.delete(self.current_user)
self.redirect("/packages/%s" % monitoring.name)
@base.authenticated
async def post(self, slug, name):
# Fetch the distribution
- distro = self.backend.distros.get_by_slug(slug)
+ distro = await 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)
+ monitoring = await 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))
if not monitoring.has_perm(self.current_user):
raise tornado.web.HTTPError(403)
- # Perform check (it is already starting its own transaction)
+ # Perform check
await monitoring.check()
# Redirect back