web_PYTHON = \
src/web/__init__.py \
+ src/web/api.py \
+ src/web/auth.py \
src/web/base.py \
+ src/web/builders.py \
+ src/web/builds.py \
+ src/web/distributions.py \
src/web/errors.py \
src/web/handlers.py \
- src/web/handlers_api.py \
- src/web/handlers_auth.py \
- src/web/handlers_builders.py \
- src/web/handlers_builds.py \
- src/web/handlers_distro.py \
- src/web/handlers_jobs.py \
- src/web/handlers_keys.py \
- src/web/handlers_packages.py \
- src/web/handlers_search.py \
- src/web/handlers_updates.py \
- src/web/handlers_users.py \
+ src/web/jobs.py \
+ src/web/keys.py \
src/web/mirrors.py \
- src/web/ui_modules.py
+ src/web/packages.py \
+ src/web/search.py \
+ src/web/ui_modules.py \
+ src/web/updates.py \
+ src/web/users.py
webdir = $(buildservicedir)/web
src/templates/job-schedule-rebuild.html \
src/templates/job-schedule-test.html \
src/templates/jobs-detail.html \
- src/templates/jobs-filter.html \
- src/templates/jobs-index.html \
src/templates/keys-delete.html \
src/templates/keys-import.html \
src/templates/keys-list.html \
src/templates/package-detail.html \
src/templates/package-detail-list.html \
src/templates/package-properties.html \
- src/templates/packages-list.html \
+ src/templates/queue.html \
src/templates/register-activation-fail.html \
src/templates/register-activation-success.html \
src/templates/register-fail.html \
src/templates/modules/build-table.html \
src/templates/modules/comments-table.html \
src/templates/modules/commits-table.html \
+ src/templates/modules/commit-message.html \
src/templates/modules/files-table.html \
src/templates/modules/footer.html \
src/templates/modules/jobs-table.html \
+ src/templates/modules/link-to-user.html \
src/templates/modules/log-entry-comment.html \
src/templates/modules/log-entry.html \
src/templates/modules/log-entry-small.html \
src/templates/modules/log-files-table.html \
src/templates/modules/log.html \
src/templates/modules/log-table.html \
- src/templates/modules/maintainer.html \
src/templates/modules/modal-base.html \
src/templates/modules/modal-build-comment.html \
src/templates/modules/modal-build-push.html \
src/templates/modules/repo-actions-table.html \
src/templates/modules/repository-table.html \
src/templates/modules/source-table.html \
+ src/templates/modules/text.html \
src/templates/modules/updates-table.html \
src/templates/modules/user-table.html \
src/templates/modules/watchers-sidebar-table.html
dist_templates_packages_DATA = \
src/templates/packages/changelog.html \
+ src/templates/packages/index.html \
src/templates/packages/view-file.html
templates_packagesdir = $(templatesdir)/packages
static_fontdir = $(staticdir)/font
dist_static_images_DATA = \
- src/static/images/icons/build-dependency_error.png \
src/static/images/icons/build-dispatching.png \
src/static/images/icons/build-failed.png \
src/static/images/icons/build-finished.png \
src/templates/job-schedule-rebuild.html
src/templates/job-schedule-test.html
src/templates/jobs-detail.html
-src/templates/jobs-filter.html
-src/templates/jobs-index.html
src/templates/keys-delete.html
src/templates/keys-import.html
src/templates/keys-list.html
src/templates/packages/builds/scratch.html
src/templates/packages/builds/times.html
src/templates/packages/changelog.html
-src/templates/packages-list.html
+src/templates/packages/index.html
src/templates/packages/view-file.html
+src/templates/queue.html
src/templates/register-activation-fail.html
src/templates/register-activation-success.html
src/templates/register-fail.html
src/templates/user-profile-passwd-ok.html
src/hub/handlers.py
src/hub/__init__.py
-src/web/handlers_api.py
-src/web/handlers_auth.py
-src/web/handlers_base.py
-src/web/handlers_builders.py
-src/web/handlers_builds.py
-src/web/handlers_distro.py
-src/web/handlers_jobs.py
-src/web/handlers_keys.py
-src/web/handlers_packages.py
-src/web/handlers.py
-src/web/handlers_search.py
-src/web/handlers_updates.py
-src/web/handlers_users.py
src/web/__init__.py
+src/web/api.py
+src/web/auth.py
+src/web/base.py
+src/web/builders.py
+src/web/builds.py
+src/web/distributions.py
+src/web/handlers.py
+src/web/jobs.py
+src/web/keys.py
src/web/mirrors.py
+src/web/packages.py
+src/web/search.py
src/web/ui_modules.py
+src/web/updates.py
+src/web/users.py
import logging
import os
import pakfire
-import shutil
from . import arches
from . import bugtracker
WHERE (not_before IS NULL OR not_before <= NOW())")
for row in query:
+ if not row.path:
+ continue
+
path = row.path
- if not path or not paths.startswith("%s/" % PAKFIRE_DIR):
+ if not path or not path.startswith("%s/" % PAKFIRE_DIR):
log.warning("Cannot delete file outside of the tree")
continue
try:
logging.debug("Removing %s..." % path)
- shutil.rmtree(path)
- except shutil.Error as e:
+ os.unlink(path)
+ except OSError, e:
logging.error("Could not remove %s: %s" % (path, e))
- continue
+
+ while True:
+ path = os.path.dirname(path)
+
+ # Stop if we are running outside of the tree.
+ if not path.startswith(PAKFIRE_DIR):
+ break
+
+ # If the directory is not empty, we cannot remove it.
+ if os.path.exists(path) and os.listdir(path):
+ break
+
+ try:
+ logging.debug("Removing %s..." % path)
+ os.rmdir(path)
+ except OSError, e:
+ logging.error("Could not remove %s: %s" % (path, e))
+ break
self.db.execute("DELETE FROM queue_delete WHERE id = %s", row.id)
return self._get_builder("SELECT * FROM builders \
WHERE name = %s AND deleted IS FALSE", name)
+ def get_for_arch(self, arch):
+ # noarch can be built on any builder
+ if arch == "noarch":
+ return self
+
+ builds = self._get_builders("SELECT builders.* FROM builders \
+ LEFT JOIN arches_compat ON builders.cpu_arch = arches_compat.native_arch \
+ WHERE (builders.cpu_arch = %s OR arches_compat.build_arch = %s) \
+ AND builders.deleted IS FALSE", arch, arch)
+
+ return builds
+
def get_history(self, limit=None, offset=None, builder=None, user=None):
query = "SELECT * FROM builders_history"
args = []
description = property(lambda s: s.data.description or "", set_description)
+ def is_online(self):
+ """
+ Returns True if the builder is online
+ """
+ return self.keepalive >= datetime.datetime.utcnow() - datetime.timedelta(minutes=1)
+
@property
def keepalive(self):
"""
def passphrase(self):
return self.data.passphrase
+ @property
+ def performance_index(self):
+ """
+ Returns a number that determines how "fast" the builder is
+ """
+ # XXX needs to be something better
+ index = self.cpu_bogomips
+
+ # We devide the performance index by the number of already running
+ # builds to avoid that the fastest builder always gets all the jobs
+ index /= len(self.active_jobs) + 1
+
+ return index
+
# Load average
@property
return "online"
@lazy_property
- def active_jobs(self, *args, **kwargs):
- return self.pakfire.jobs.get_active(builder=self, *args, **kwargs)
+ def active_jobs(self):
+ jobs = self.backend.jobs._get_jobs("SELECT jobs.* FROM jobs \
+ WHERE time_started IS NOT NULL AND time_finished IS NULL \
+ AND builder_id = %s ORDER BY time_started", self.id)
+
+ return list(jobs)
@property
def too_many_jobs(self):
# Don't return anything if the builder has already too many jobs running
if self.too_many_jobs:
+ logging.debug("%s has too many jobs running" % self)
return
for job in self.jobqueue:
+ logging.debug("Looking at %s..." % job)
# Only allow building test jobs in test mode
if self.testmode and not job.test:
continue
+ # If we are the fastest builder to handle this job, we will
+ # get it.
+ if job.candidate_builders:
+ fastest_builder = job.candidate_builders.pop(0)
+
+ if not self == fastest_builder:
+ logging.debug("We are not the fastest builder for this job (%s is)" % fastest_builder)
+ continue
+
return job
def get_history(self, *args, **kwargs):
# Obsolete all other builds with the same name to track updates.
build.obsolete_others()
- # Search for possible bug IDs in the commit message.
- build.search_for_bugs()
-
return build
def create_from_source_package(self, filename, distro, commit=None, type="release",
# Create a new build object from the package
build = self.create(pkg, type=type, owner=owner, distro=distro)
+ if commit:
+ # Import any fixed bugs
+ for bug in commit.fixed_bugs:
+ build.add_bug(bug)
+
+ # Upvote the build for the testers
+ for tester in commit.testers:
+ build.upvote(tester)
+
# Create all automatic jobs
build.create_autojobs(arches=arches)
remove = True
for job in self.jobs:
- if job.state in ("new", "pending", "running", "dependency_error"):
+ if job.state in ("pending", "running"):
job.state = "aborted"
# If this build is in a repository, it will leave it.
if self.pkg and self.pkg.commit:
return self.pkg.commit
- def update_message(self, message):
- self._set_attribute("message", message)
-
def has_perm(self, user):
"""
Check, if the given user has the right to perform administrative
return res.score or 0
+ def upvote(self, user, score=1):
+ # Creates an empty comment with a score
+ self.db.execute("INSERT INTO builds_comments(build_id, user_id, score) \
+ VALUES(%s, %s, %s)", self.id, user.id, score)
+
+ # Update cache
+ self.score += score
+
def get_commenters(self):
users = self.db.query("SELECT DISTINCT users.id AS id FROM builds_comments \
JOIN users ON builds_comments.user_id = users.id \
query = self.db.get("SELECT NOW() - time_added AS duration FROM repositories_builds \
WHERE build_id = %s", self.id)
- duration = query.duration
- if duration >= self.repo.time_min:
- return True
-
- return False
+ return query.duration.total_seconds() >= self.repo.time_min
## Bugs
if log:
self.log("bug_removed", user=user, bug_id=bug_id)
- def search_for_bugs(self):
- if not self.commit:
- return
-
- pattern = re.compile(r"(bug\s?|#)(\d+)")
-
- for txt in (self.commit.subject, self.commit.message):
- for bug in re.finditer(pattern, txt):
- try:
- bugid = int(bug.group(2))
- except ValueError:
- continue
-
- # Check if a bug with the given ID exists in BZ.
- bug = self.backend.bugzilla.get_bug(bugid)
- if not bug:
- continue
-
- self.add_bug(bugid)
-
def get_bugs(self):
bugs = []
for bug_id in self.get_bug_ids():
break
def check_build_dependencies(self):
- jobs = self.backend.jobs._get_jobs("SELECT * FROM jobs \
- WHERE state = 'new' OR \
- (state = 'dependency_error' AND time_finished < NOW() - '5 minutes'::interval) \
- ORDER BY time_finished LIMIT 50")
+ # Check all jobs that have never being checked before
+ self._check_build_dependencies("SELECT * FROM jobs \
+ WHERE state = %s AND dependency_check_succeeded IS NULL \
+ ORDER BY time_created", "pending")
+
+ # Redo the check for all jobs that have recently failed
+ self._check_build_dependencies("SELECT * FROM jobs \
+ WHERE state = %s AND dependency_check_succeeeded IS FALSE \
+ AND dependency_check_at < NOW() - '5 minutes'::interval \
+ ORDER BY dependency_check_at", "pending")
+
+ def _check_build_dependencies(self, query, *args):
+ jobs = self.backend.jobs._get_jobs(query, *args)
for job in jobs:
with self.db.transaction():
- # Resolve the dependencies
- job.resolvdep()
\ No newline at end of file
+ job.resolvdep()
def get_by_uuid(self, uuid):
return self._get_job("SELECT * FROM jobs WHERE uuid = %s", uuid)
- def get_active(self, host_id=None, builder=None, states=None):
- if builder:
- host_id = builder.id
-
- if states is None:
- states = ["dispatching", "running", "uploading"]
-
- query = "SELECT * FROM jobs WHERE state IN (%s)" % ", ".join(["%s"] * len(states))
- args = states
-
- if host_id:
- query += " AND builder_id = %s" % host_id
-
- query += " ORDER BY \
- CASE \
- WHEN jobs.state = 'running' THEN 0 \
- WHEN jobs.state = 'uploading' THEN 1 \
- WHEN jobs.state = 'dispatching' THEN 2 \
- WHEN jobs.state = 'pending' THEN 3 \
- WHEN jobs.state = 'new' THEN 4 \
- END, time_started ASC"
-
- return [Job(self.backend, j.id, j) for j in self.db.query(query, *args)]
-
- def get_latest(self, arch=None, builder=None, limit=None, age=None, date=None):
- query = "SELECT * FROM jobs"
- args = []
-
- where = ["(state = 'finished' OR state = 'failed' OR state = 'aborted')"]
-
- if arch:
- where.append("arch = %s")
- args.append(arch)
-
- if builder:
- where.append("builder_id = %s")
- args.append(builder.id)
-
- if date:
- try:
- year, month, day = date.split("-", 2)
- date = datetime.date(int(year), int(month), int(day))
- except ValueError:
- pass
- else:
- where.append("(time_created::date = %s OR \
- time_started::date = %s OR time_finished::date = %s)")
- args += (date, date, date)
-
- if age:
- where.append("time_finished >= NOW() - '%s'::interval" % age)
-
- if where:
- query += " WHERE %s" % " AND ".join(where)
+ def get_active(self, limit=None):
+ jobs = self._get_jobs("SELECT jobs.* FROM jobs \
+ WHERE time_started IS NOT NULL AND time_finished IS NULL \
+ ORDER BY time_started LIMIT %s", limit)
- query += " ORDER BY time_finished DESC"
+ return jobs
- if limit:
- query += " LIMIT %s"
- args.append(limit)
+ def get_recently_ended(self, limit=None):
+ jobs = self._get_jobs("SELECT jobs.* FROM jobs \
+ WHERE time_finished IS NOT NULL ORDER BY time_finished DESC LIMIT %s", limit)
- return [Job(self.backend, j.id, j) for j in self.db.query(query, *args)]
+ return jobs
def restart_failed(self):
jobs = self._get_jobs("SELECT jobs.* FROM jobs \
return res.len
+ @property
+ def uuid(self):
+ return self.data.uuid
+
+ @property
+ def name(self):
+ return "%s-%s.%s" % (self.pkg.name, self.pkg.friendly_version, self.arch)
+
+ @property
+ def build_id(self):
+ return self.data.build_id
+
+ @lazy_property
+ def build(self):
+ return self.backend.builds.get_by_id(self.build_id)
+
+ @property
+ def test(self):
+ return self.data.test
+
+ @property
+ def related_jobs(self):
+ ret = []
+
+ for job in self.build.jobs:
+ if job == self:
+ continue
+
+ ret.append(job)
+
+ return ret
+
+ @property
+ def pkg(self):
+ return self.build.pkg
+
+ @property
+ def size(self):
+ return sum((p.size for p in self.packages))
+
+ @lazy_property
+ def rank(self):
+ """
+ Returns the rank in the build queue
+ """
+ if not self.state == "pending":
+ return
+
+ res = self.db.get("SELECT rank FROM jobs_queue WHERE job_id = %s", self.id)
+
+ if res:
+ return res.rank
+
@property
def distro(self):
return self.build.distro
- def restart(self):
- # Copy the job and let it build again
- return self.backend.jobs.create(self.build, self.arch,
- test=self.test, superseeds=self)
-
def get_superseeded_by(self):
if self.data.superseeded_by:
return self.backend.jobs.get_by_id(self.data.superseeded_by)
assert isinstance(superseeded_by, self.__class__)
self._set_attribute("superseeded_by", superseeded_by.id)
- self.superseeded_by = superseeded_by
superseeded_by = lazy_property(get_superseeded_by, set_superseeded_by)
+ def start(self, builder):
+ """
+ Starts this job on builder
+ """
+ self.builder = builder
+
+ # Start to dispatch the build job
+ self.state = "dispatching"
+
+ def running(self):
+ self.state = "running"
+
+ # Set start time
+ self.time_started = datetime.datetime.utcnow()
+ self.time_finished = None
+
+ def finished(self):
+ self.state = "finished"
+
+ # Log end time
+ self.time_finished = datetime.datetime.utcnow()
+
+ # Notify users
+ self.send_finished_message()
+
+ def failed(self, message):
+ self.state = "failed"
+ self.message = message
+
+ # Log end time
+ self.time_finished = datetime.datetime.utcnow()
+
+ # Notify users
+ self.send_failed_message()
+
+ def restart(self, test=None, start_not_before=None):
+ # Copy the job and let it build again
+ job = self.backend.jobs.create(self.build, self.arch,
+ test=test or self.test, superseeds=self)
+
+ if start_not_before:
+ job.start_not_before = start_not_before
+
+ return job
+
def delete(self):
"""
Deletes a job from the database
return entries
- @property
- def uuid(self):
- return self.data.uuid
-
- @property
- def test(self):
- return self.data.test
-
- @property
- def build_id(self):
- return self.data.build_id
-
- @lazy_property
- def build(self):
- return self.backend.builds.get_by_id(self.build_id)
-
- @property
- def related_jobs(self):
- ret = []
-
- for job in self.build.jobs:
- if job == self:
- continue
-
- ret.append(job)
-
- return ret
-
- @property
- def pkg(self):
- return self.build.pkg
-
- @property
- def name(self):
- return "%s-%s.%s" % (self.pkg.name, self.pkg.friendly_version, self.arch)
-
- @property
- def size(self):
- return sum((p.size for p in self.packages))
-
- @lazy_property
- def rank(self):
- """
- Returns the rank in the build queue
- """
- if not self.state == "pending":
- return
-
- res = self.db.get("SELECT rank FROM jobs_queue WHERE job_id = %s", self.id)
-
- if res:
- return res.rank
-
def is_running(self):
"""
Returns True if job is in a running state.
def get_state(self):
return self.data.state
- def set_state(self, state, user=None, log=True):
- # Nothing to do if the state remains.
- if not self.state == state:
- self._set_attribute("state", state)
-
- # Log the event.
- if log and not state == "new":
- self.log("state_change", state=state, user=user)
-
- # Always clear the message when the status is changed.
- self.update_message(None)
+ def set_state(self, state):
+ self._set_attribute("state", state)
- # Update some more informations.
- if state == "dispatching":
- # Set start time.
- self._set_attribute("time_started", datetime.datetime.utcnow())
-
- elif state in ("aborted", "dependency_error", "finished", "failed"):
- self._set_attribute("time_finished", datetime.datetime.utcnow())
-
- # Send messages to the user.
- if state == "finished":
- self.send_finished_message()
-
- elif state == "failed":
- # Remove all package files if a job is set to failed state.
- self.__delete_packages()
-
- self.send_failed_message()
-
- # Automatically update the state of the build (not on test builds).
+ # Automatically update the state of the build (not on test builds)
if not self.test:
self.build.auto_update_state()
state = property(get_state, set_state)
- @property
- def message(self):
- return self.data.message
+ def set_message(self, message):
+ if message:
+ message = "%s" % message
- def update_message(self, message):
self._set_attribute("message", message)
+ message = property(lambda s: s.data.message, set_message)
+
def get_builder(self):
if self.data.builder_id:
return self.backend.builders.get_by_id(self.data.builder_id)
builder = lazy_property(get_builder, set_builder)
+ @lazy_property
+ def candidate_builders(self):
+ """
+ Returns all active builders that could build this job
+ """
+ builders = self.backend.builders.get_for_arch(self.arch)
+
+ # Remove all builders that are not available
+ builders = (b for b in builders if b.enabled and b.is_online())
+
+ # Sort them by the fastest builder first
+ return sorted(builders, key=lambda b: -b.performance_index)
+
@property
def arch(self):
return self.data.arch
def time_created(self):
return self.data.time_created
- @property
- def time_started(self):
- return self.data.time_started
+ def set_time_started(self, time_started):
+ self._set_attribute("time_started", time_started)
- @property
- def time_finished(self):
- return self.data.time_finished
+ time_started = property(lambda s: s.data.time_started, set_time_started)
+
+ def set_time_finished(self, time_finished):
+ self._set_attribute("time_finished", time_finished)
+
+ time_finished = property(lambda s: s.data.time_finished, set_time_finished)
+
+ def set_start_not_before(self, start_not_before):
+ self._set_attribute("start_not_before", start_not_before)
+
+ start_not_before = property(lambda s: s.data.start_not_before, set_start_not_before)
def get_pkg_by_uuid(self, uuid):
pkg = self.backend.packages._get_package("SELECT packages.id FROM packages \
self.backend.messages.send_to_all(self.message_recipients,
MSG_BUILD_FAILED_SUBJECT, MSG_BUILD_FAILED, info)
- def set_start_time(self, start_not_before):
- self._set_attribute("start_not_before", start_not_before)
-
- def schedule(self, type, start_time=None, user=None):
- assert type in ("rebuild", "test")
-
- if type == "rebuild":
- if self.state == "finished":
- return
-
- job = self.restart()
- job.set_start_time(start_time)
-
- # Log the event.
- self.log("schedule_rebuild", user=user)
-
- elif type == "test":
- if not self.state == "finished":
- return
-
- # Create a new job with same build and arch.
- job = self.create(self.backend, self.build, self.arch, test=True)
- job.set_start_time(start_time)
-
- # Log the event.
- self.log("schedule_test_job", test_job=job, user=user)
-
- return job
-
- def schedule_test(self, start_not_before=None, user=None):
- # XXX to be removed
- return self.schedule("test", start_time=start_not_before, user=user)
-
- def schedule_rebuild(self, start_not_before=None, user=None):
- # XXX to be removed
- return self.schedule("rebuild", start_time=start_not_before, user=user)
-
def get_build_repos(self):
"""
Returns a list of all repositories that should be used when
return "\n\n".join(confs)
+ def set_dependency_check_succeeded(self, value):
+ self._set_attribute("dependency_check_succeeded", value)
+ self._set_attribute("dependency_check_at", datetime.datetime.utcnow())
+
+ # Reset the message
+ if value is True:
+ self.message = None
+
+ dependency_check_succeeded = property(
+ lambda s: s.data.dependency_check_succeeded,
+ set_dependency_check_succeeded)
+
def resolvdep(self):
+ log.info("Processing dependencies for %s..." % self)
+
config = pakfire.config.Config(files=["general.conf"])
config.parse(self.get_config(local=True))
# Catch dependency errors and log the problem string.
except DependencyError, e:
- self.state = "dependency_error"
- self.update_message("%s" % e)
+ self.dependency_check_succeeded = False
+ self.message = e
+ # The dependency check has succeeded
else:
- # If the build dependencies can be resolved, we set the build in
- # pending state.
- if solver.status is True:
- if self.state in ("failed",):
- return
-
- self.state = "pending"
+ self.dependency_check_succeeded = True
def auth(self, username, password):
log.debug("Checking credentials for %s" % username)
- dn = self.get_dn_by_uid(username)
+ dn = self.get_dn(username)
if not dn:
- log.debug("Could not resolve username %s to dn" % username)
+ log.debug("Could not resolve %s to dn" % username)
return False
return self.bind(dn, password)
log.debug("DN for uid %s is: %s" % (uid, dn))
return dn
- def get_user(self, uid, **kwargs):
+ def get_dn_by_mail(self, mail):
+ result = self.search("(&(objectClass=posixAccount)(mail=%s))" % mail, limit=1, attrlist=["uid"])
+
+ for dn, attrs in result:
+ return dn
+
+ log.debug("DN for mail %s is: %s" % (mail, dn))
+ return None
+
+ def get_dn(self, name):
+ return self.get_dn_by_uid(name) or self.get_dn_by_mail(name)
+
+ def get_user_by_mail(self, mail, **kwargs):
+ result = self.search("(&(objectClass=posixAccount)(mail=%s))" % mail, limit=1, **kwargs)
+ for dn, attrs in result:
+ return (dn, attrs)
+
+ return None
+
+ def get_user_by_dn(self, uid, **kwargs):
result = self.search("(&(objectClass=posixAccount)(uid=%s))" % uid, limit=1, **kwargs)
for dn, attrs in result:
return (dn, attrs)
- return (None, None)
\ No newline at end of file
+ return None
+
+ def get_user(self, name, **kwargs):
+ return self.get_user_by_dn(name, **kwargs) or self.get_user_by_mail(name, **kwargs)
return self._get_package("SELECT * FROM packages \
WHERE id = %s", pkg_id)
- def get_all_names(self, user=None, states=None):
- query = "SELECT DISTINCT packages.name AS name, summary FROM packages \
- JOIN builds ON builds.pkg_id = packages.id \
- WHERE packages.type = 'source'"
-
- conditions = []
- args = []
-
- if user and not user.is_admin():
- conditions.append("builds.owner_id = %s")
- args.append(user.id)
-
- if states:
- for state in states:
- conditions.append("builds.state = %s")
- args.append(state)
-
- if conditions:
- query += " AND (%s)" % " OR ".join(conditions)
-
- query += " ORDER BY packages.name"
-
- return [(n.name, n.summary) for n in self.db.query(query, *args)]
+ def get_list(self):
+ """
+ Returns a list with all package names and the summary line
+ that have at one time been part of the distribution
+ """
+ return self.db.query("SELECT DISTINCT packages.name AS name, packages.summary AS summary FROM builds \
+ LEFT JOIN packages ON builds.pkg_id = packages.id \
+ WHERE builds.type = %s AND builds.state != %s", "release", "obsolete")
def get_by_uuid(self, uuid):
pkg = self.db.get("SELECT * FROM packages WHERE uuid = %s LIMIT 1", uuid)
def delete(self):
self.backend.delete_file(os.path.join(PACKAGES_DIR, self.path))
+ self.db.execute("DELETE FROM packages_deps WHERE pkg_id = %s", self.id)
+
# Delete all files from the filelist.
self.db.execute("DELETE FROM filelists WHERE pkg_id = %s", self.id)
"[repo:%s]" % self.identifier,
"description = %s - %s" % (self.distro.name, self.summary),
"enabled = 1",
- "baseurl = %s/%{arch}" % (self.path if local else self.url),
+ "baseurl = %s/%%{arch}" % (self.path if local else self.url),
]
if self.mirrored and not local:
import os
import pakfire
import pakfire.config
+import re
import shutil
import subprocess
import tempfile
from .constants import *
from .decorators import *
+VALID_TAGS = (
+ "Acked-by",
+ "Cc",
+ "Fixes",
+ "Reported-by",
+ "Reviewed-by",
+ "Signed-off-by",
+ "Suggested-by",
+ "Tested-by",
+)
+
class Sources(base.Object):
def _get_source(self, query, *args):
res = self.db.get(query, *args)
state = property(lambda s: s.data.state, set_state)
- @property
+ @lazy_property
def author(self):
- return self.data.author
+ return self.backend.users.find_maintainer(self.data.author) or self.data.author
- @property
+ @lazy_property
def committer(self):
- return self.data.committer
+ return self.backend.users.find_maintainer(self.data.committer) or self.data.committer
@property
def subject(self):
return self.data.subject.strip()
@property
- def message(self):
+ def body(self):
return self.data.body.strip()
+ @lazy_property
+ def message(self):
+ """
+ Returns the message without any Git tags
+ """
+ # Compile regex
+ r = re.compile("^(%s):?" % "|".join(VALID_TAGS), re.IGNORECASE)
+
+ message = []
+ for line in self.body.splitlines():
+ # Find lines that start with a known Git tag
+ if r.match(line):
+ continue
+
+ message.append(line)
+
+ # If all lines are empty lines, we send back an empty message
+ if all((l == "" for l in message)):
+ return
+
+ # We will now break the message into paragraphs
+ paragraphs = re.split("\n\n+", "\n".join(message))
+ print paragraphs
+
+ message = []
+ for paragraph in paragraphs:
+ # Remove all line breaks that are not following a colon
+ # and where the next line does not start with a star.
+ paragraph = re.sub("(?<=\:)\n(?=[\*\s])", " ", paragraph)
+
+ message.append(paragraph)
+
+ return "\n\n".join(message)
+
@property
def message_full(self):
- msg = [self.subject, ""] + self.message.splitlines()
+ message = self.subject
+
+ if self.message:
+ message += "\n\n%s" % self.message
+
+ return message
- return "\n".join(msg)
+ def get_tag(self, tag):
+ """
+ Returns a list of the values of this Git tag
+ """
+ if not tag in VALID_TAGS:
+ raise ValueError("Unknown tag: %s" % tag)
+
+ # Compile regex
+ r = re.compile("^%s:? (.*)$" % tag, re.IGNORECASE)
+
+ values = []
+ for line in self.body.splitlines():
+ # Skip all empty lines
+ if not line:
+ continue
+
+ # Check if line matches the regex
+ m = r.match(line)
+ if m:
+ values.append(m.group(1))
+
+ return values
+
+ @lazy_property
+ def contributors(self):
+ contributors = [
+ self.data.author,
+ self.data.committer,
+ ]
+
+ for tag in ("Acked-by", "Cc", "Reported-by", "Reviewed-by", "Signed-off-by", "Suggested-by", "Tested-by"):
+ contributors += self.get_tag(tag)
+
+ # Get all user accounts that we know
+ users = self.backend.users.find_maintainers(contributors)
+
+ # Add all email addresses where a user could not be found
+ for contributor in contributors[:]:
+ for user in users:
+ if user.has_email_address(contributor):
+ try:
+ contributors.remove(contributor)
+ except:
+ pass
+
+ return sorted(contributors + users)
+
+ @lazy_property
+ def testers(self):
+ users = []
+
+ for tag in ("Acked-by", "Reviewed-by", "Signed-off-by", "Tested-by"):
+ users += self.get_tag(tag)
+
+ return self.backend.users.find_maintainers(users)
+
+ @property
+ def fixed_bugs(self):
+ """
+ Returns a list of all fixed bugs
+ """
+ return self.get_tag("Fixes")
@property
def date(self):
if None in (name, password):
return
- # Search for the username in the database.
- # The user must not be deleted and must be activated.
- user = self._get_user("SELECT * FROM users WHERE name = %s AND \
- activated IS TRUE AND deleted IS FALSE", name)
+ # usually we will get an email address as name
+ user = self.get_by_email(name) or self.get_by_name(name)
- # If no user could be found, we search for a matching user in
- # the LDAP database
if not user:
+ # If no user could be found, we search for a matching user in
+ # the LDAP database
if not self.ldap.auth(name, password):
return
# If a LDAP user is found (and password matches), we will
# create a new local user with the information from LDAP.
- user = self.register_from_ldap(name)
+ user = self.create_from_ldap(name)
+
+ if not user.activated or user.deleted:
+ return
# Check if the password matches
if user.check_password(password):
LEFT JOIN users_emails ON users.id = users_emails.user_id \
WHERE users_emails.email = %s", email)
+ def find_maintainers(self, maintainers):
+ email_addresses = []
+
+ # Make a unique list of all email addresses
+ for maintainer in maintainers:
+ name, email_address = email.utils.parseaddr(maintainer)
+
+ if not email_address in email_addresses:
+ email_addresses.append(email_address)
+
+ users = self._get_users("SELECT DISTINCT users.* FROM users \
+ LEFT JOIN users_emails ON users.id = users_emails.user_id \
+ WHERE users_emails.activated IS TRUE \
+ AND users_emails.email = ANY(%s)", email_addresses)
+
+ return sorted(users)
+
def find_maintainer(self, s):
name, email_address = email.utils.parseaddr(s)
WHERE user_id = %s AND email = %s AND activated IS TRUE",
self.id, email)
+ def has_email_address(self, email_address):
+ try:
+ mail, email_address = email.utils.parseaddr(email_address)
+ except:
+ pass
+
+ return email_address in self.emails
+
def activate_email(self, code):
# Search email by activation code
email = self.backend.users._get_user_email("SELECT * FROM users_emails \
def activated(self):
return self.data.activated
+ @property
+ def deleted(self):
+ return self.data.deleted
+
@property
def registered(self):
return self.data.registered
ALTER TYPE jobs_history_state OWNER TO pakfire;
---
--- Name: jobs_state; Type: TYPE; Schema: public; Owner: pakfire
---
-
-CREATE TYPE jobs_state AS ENUM (
- 'new',
- 'pending',
- 'running',
- 'finished',
- 'dispatching',
- 'uploading',
- 'failed',
- 'aborted',
- 'temporary_failed',
- 'dependency_error',
- 'download_error',
- 'deleted'
-);
-
-
-ALTER TYPE jobs_state OWNER TO pakfire;
-
--
-- Name: mirrors_history_action; Type: TYPE; Schema: public; Owner: pakfire
--
id integer NOT NULL,
build_id integer NOT NULL,
user_id integer NOT NULL,
- text text NOT NULL,
+ text text,
score integer NOT NULL,
- time_created timestamp without time zone NOT NULL,
+ time_created timestamp without time zone DEFAULT now() NOT NULL,
time_updated timestamp without time zone
);
id integer NOT NULL,
uuid text NOT NULL,
build_id integer NOT NULL,
- state jobs_state DEFAULT 'new'::jobs_state NOT NULL,
+ state text DEFAULT 'pending'::text NOT NULL,
arch text NOT NULL,
time_created timestamp without time zone DEFAULT now() NOT NULL,
time_started timestamp without time zone,
aborted_state integer DEFAULT 0 NOT NULL,
message text,
test boolean DEFAULT true NOT NULL,
- superseeded_by integer
+ superseeded_by integer,
+ dependency_check_succeeded boolean,
+ dependency_check_at timestamp without time zone,
+ CONSTRAINT jobs_states CHECK ((state = ANY (ARRAY['pending'::text, 'dispatching'::text, 'running'::text, 'uploading'::text, 'finished'::text, 'aborted'::text, 'download_error'::text, 'failed'::text])))
);
jobs.arch,
date_part('epoch'::text, (jobs.time_finished - jobs.time_started)) AS duration
FROM jobs
- WHERE ((jobs.test IS FALSE) AND (jobs.state = 'finished'::jobs_state));
+ WHERE ((jobs.test IS FALSE) AND (jobs.state = 'finished'::text));
ALTER TABLE builds_times OWNER TO pakfire;
ALTER SEQUENCE images_types_id_seq OWNED BY images_types.id;
---
--- Name: jobs_active; Type: VIEW; Schema: public; Owner: pakfire
---
-
-CREATE VIEW jobs_active AS
- SELECT jobs.id,
- jobs.uuid,
- jobs.build_id,
- jobs.state,
- jobs.arch,
- jobs.time_created,
- jobs.time_started,
- jobs.time_finished,
- jobs.start_not_before,
- jobs.builder_id,
- jobs.aborted_state,
- jobs.message
- FROM jobs
- WHERE (jobs.state = ANY (ARRAY['dispatching'::jobs_state, 'running'::jobs_state, 'uploading'::jobs_state]))
- ORDER BY jobs.time_started;
-
-
-ALTER TABLE jobs_active OWNER TO pakfire;
-
--
-- Name: jobs_buildroots; Type: TABLE; Schema: public; Owner: pakfire; Tablespace:
--
rank() OVER (ORDER BY (NOT jobs.test), builds.priority DESC, jobs.time_created) AS rank
FROM (jobs
LEFT JOIN builds ON ((jobs.build_id = builds.id)))
- WHERE (jobs.state = 'pending'::jobs_state)
+ WHERE ((jobs.state = 'pending'::text) AND (jobs.dependency_check_succeeded IS TRUE))
)
SELECT queue.id AS job_id,
queue.rank
CREATE INDEX idx_2198063_state ON jobs USING btree (state);
---
--- Name: idx_2198063_time_finished; Type: INDEX; Schema: public; Owner: pakfire; Tablespace:
---
-
-CREATE INDEX idx_2198063_time_finished ON jobs USING btree (time_finished);
-
-
--
-- Name: idx_2198063_uuid; Type: INDEX; Schema: public; Owner: pakfire; Tablespace:
--
CREATE INDEX jobs_buildroots_pkg_uuid ON jobs_buildroots USING btree (pkg_uuid);
+--
+-- Name: jobs_queue_ready; Type: INDEX; Schema: public; Owner: pakfire; Tablespace:
+--
+
+CREATE INDEX jobs_queue_ready ON jobs USING btree (id) WHERE ((state = 'new'::text) AND (dependency_check_succeeded IS TRUE));
+
+
+--
+-- Name: jobs_time_finished; Type: INDEX; Schema: public; Owner: pakfire; Tablespace:
+--
+
+CREATE INDEX jobs_time_finished ON jobs USING btree (time_finished DESC) WHERE (time_finished IS NOT NULL);
+
+
+--
+-- Name: jobs_time_started; Type: INDEX; Schema: public; Owner: pakfire; Tablespace:
+--
+
+CREATE INDEX jobs_time_started ON jobs USING btree (time_started) WHERE ((time_started IS NOT NULL) AND (time_finished IS NULL));
+
+
--
-- Name: mirrors_checks_sort; Type: INDEX; Schema: public; Owner: pakfire; Tablespace:
--
ADD CONSTRAINT jobs_repos_repo_id FOREIGN KEY (repo_id) REFERENCES repositories(id);
+--
+-- Name: jobs_superseeded_by; Type: FK CONSTRAINT; Schema: public; Owner: pakfire
+--
+
+ALTER TABLE ONLY jobs
+ ADD CONSTRAINT jobs_superseeded_by FOREIGN KEY (superseeded_by) REFERENCES jobs(id);
+
+
--
-- Name: keys_subkeys_key_id; Type: FK CONSTRAINT; Schema: public; Owner: pakfire
--
limit = self.get_argument_int("limit", 5)
# Get the latest jobs.
- jobs = self.backend.jobs.get_latest(age="24 HOUR", limit=limit)
+ jobs = self.backend.jobs.get_recently_ended(limit=limit)
args = {
"jobs" : [self.job2json(j) for j in jobs],
return self.add_timeout(10, self.callback)
# We got a job!
- job.state = "dispatching"
-
- # Set our build host.
- job.builder = self.builder
+ job.start(builder=self.builder)
ret = {
"id" : job.uuid,
if not job.builder == self.builder:
raise tornado.web.HTTPError(403, "Altering another builder's build.")
- # Save information to database.
- job.state = state
-
message = self.get_argument("message", None)
- job.update_message(message)
+
+ # Save information to database.
+ with self.db.transaction():
+ if state == "running":
+ job.running()
+ elif state == "failed":
+ job.failed(message)
+ elif state == "finished":
+ job.finished()
+ else:
+ job.state = state
self.finish("OK")
if not upload.builder == self.builder:
raise tornado.web.HTTPError(403, "Using an other host's file.")
- # Remove all files that have to be deleted, first.
- self.backend.cleanup_files()
-
try:
job.add_file(upload.path)
color: yellow;
}
-table.builds tr.build td.jobs a.dependency_error {
- color: blue;
-}
-
table.builds tr.build td.jobs a.failed {
color: red;
}
<hr>
<p class="muted">
- {{ _("Author") }} {% module Maintainer(build.commit.author) %}
+ {{ _("Author") }} {% module LinkToUser(build.commit.author) %}
‐
{{ _("Commit") }} <a href="/distro/{{ build.distro.identifier }}/source/{{ build.pkg.commit.source.identifier }}/{{ build.pkg.commit.revision }}">{{ build.pkg.commit.revision[:7] }}</a>
</p>
</tr>
<tr>
<td>{{ _("Author") }}</td>
- <td>{% raw format_email(commit.author) %}</td>
+ <td>{% module LinkToUser(commit.author) %}</td>
</tr>
<tr>
<td>{{ _("Committer") }}</td>
- <td>{% raw format_email(commit.committer) %}</td>
- </tr>
- <tr>
- <td>{{ _("Subject") }}</td>
- <td>{{ commit.subject }}</td>
+ <td>{% module LinkToUser(commit.committer) %}</td>
</tr>
{% if commit.message %}
<tr>
<td colspan="2">
- {% module Text(commit.message, pre=True) %}
+ {% module CommitMessage(commit) %}
</td>
</tr>
{% end %}
<li><a class="build running" name="#">{{ _("Build is running") }}</a></li>
<li><a class="build failed" name="#">{{ _("Build has failed") }}</a></li>
<li><a class="build pending" name="#">{{ _("Build is waiting to be processed") }}</a></li>
- <li><a class="build dependency_error" name="#">{{ _("There was a dependency error when the package was built") }}</a></li>
<li><a class="build waiting" name="#">{{ _("Build is waiting for source to go to pending state") }}</a></li>
<li><a class="build dispatching" name="#">{{ _("Files of this build are transferred to the build server") }}</a></li>
<li><a class="build uploading" name="#">{{ _("Files are being uploaded to the service") }}</a></li>
<td colspan="2">
{{ _("Exception (traceback):") }}
<br><br>
- <pre>{{ "\n".join(tb) }}</pre>
+ <pre>{{ "".join(tb) }}</pre>
</td>
</tr>
{% end %}
<ul class="nav nav-pills">
<li>
- <a href="/jobs">{{ _("Show more build jobs") }}</a>
+ <a href="/queue">
+ {{ _("Job Queue") }}
+
+ <!-- should be a badge -->
+ ({{ len(backend.jobqueue) }})
+ </a>
</li>
</ul>
{% end %}
+++ /dev/null
-{% extends "base.html" %}
-
-{% block title %}{{ _("Filter jobs") }}{% end block %}
-
-{% block body %}
- <ul class="breadcrumb">
- <li>
- <a href="/">{{ _("Home") }}</a>
- <span class="divider">/</span>
- </li>
- <li>
- <a href="/jobs">{{ _("Jobs") }}</a>
- <span class="divider">/</span>
- </li>
- <li class="active">
- <a href="/jobs/filter">{{ _("Filter") }}</a>
- </li>
- </ul>
-
- <div class="page-header">
- <h2>{{ _("Filter jobs") }}</h2>
- </div>
-
- <form class="form-horizontal" method="GET" action="/jobs">
- <fieldset>
- <div class="control-group">
- <label class="control-label">{{ _("Builder") }}</label>
- <div class="controls">
- <select name="builder">
- <option value="">{{ _("[Choose one]") }}</option>
- {% for b in builders %}
- <option value="{{ b.name }}">{{ b.name }}</option>
- {% end %}
- </select>
- <span class="help-block">
- {{ _("Only show jobs, that have been built by this builder.") }}
- </span>
- </div>
- </div>
-
- <hr>
-
- <div class="control-group">
- <label class="control-label">{{ _("Architecture") }}</label>
- <div class="controls">
- <select name="arch">
- <option value="">{{ _("[Choose one]") }}</option>
- {% for a in arches %}
- <option value="{{ a.name }}">{{ a.name }}</option>
- {% end %}
- </select>
- <span class="help-block">
- {{ _("Only show jobs, with this architecture.") }}
- </span>
- </div>
- </div>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-primary">{{ _("Go!") }}</button>
- </div>
- </fieldset>
- </form>
-{% end block %}
+++ /dev/null
-{% extends "base.html" %}
-
-{% block title %}{{ _("Jobs") }}{% end block %}
-
-{% block body %}
- <ul class="breadcrumb">
- <li>
- <a href="/">{{ _("Home") }}</a>
- <span class="divider">/</span>
- </li>
- <li class="active">
- <a href="/jobs">{{ _("Jobs") }}</a>
- </li>
- </ul>
-
- <ul class="nav nav-pills pull-right">
- <li>
- <a href="/jobs/filter">{{ _("Filter jobs") }}</a>
- </li>
- </ul>
-
- <div class="page-header">
- <h2>{{ _("Jobs") }}</h2>
- </div>
-
- {% if arch or builder or date %}
- <ul>
- {% if date %}
- <li>
- {{ _("Showing only jobs from %s.") % date }}
- </li>
- {% end %}
-
- {% if builder %}
- <li>
- <a href="/builder/{{ builder.name }}">
- {{ _("Showing only builds that have been built on %s.") % builder.name }}
- </a>
- </li>
- {% end %}
-
- {% if arch %}
- <li>
- <a href="/arch/{{ arch.name }}">
- {{ _("Showing only jobs built for %s.") % arch.name }}
- </a>
- </li>
- {% end %}
- </ul>
-
- <hr>
- {% end %}
-
- {% module JobsList(jobs) %}
-{% end block %}
{% if show_user %}
{% if build.type == "scratch" and build.user %}
<td>
- {% module Maintainer(build.user) %}
+ {% module LinkToUser(build.user) %}
</td>
{% elif build.type == "release" %}
- <td>{% module Maintainer(build.pkg.maintainer) %}</td>
+ <td>{% module LinkToUser(build.pkg.maintainer) %}</td>
{% else %}
<td></td>
{% end %}
<hr>
<p class="muted">
- {{ _("Author") }} {% module Maintainer(build.commit.author) %}
+ {{ _("Author") }} {% module LinkToUser(build.commit.author) %}
<span class="pull-right">{{ locale.format_date(build.created, shorter=True) }}</span>
</p>
{% else %}
{% elif build.type == "scratch" %}
<p class="muted">
- {{ _("Owner") }} {% module Maintainer(build.owner) %}
+ {{ _("Owner") }} {% module LinkToUser(build.owner) %}
<span class="pull-right">{{ locale.format_date(build.created, shorter=True) }}</span>
</p>
--- /dev/null
+<h4>{{ commit.subject }}</h4>
+
+{% module Text(commit.message) %}
\ No newline at end of file
<a href="/job/{{ job.uuid }}">{{ job.arch }}</a>
</td>
<td>
- {% if job.state == "new" %}
- {{ _("New") }}
- {% elif job.state == "pending" %}
+ {% if job.state == "pending" %}
{{ _("Pending") }}
{% elif job.state == "failed" %}
{{ _("Failed") }}
{{ _("Running") }}
{% elif job.state == "aborted" %}
{{ _("Aborted") }}
- {% elif job.state == "dependency_error" %}
- {{ _("Dependency error") }}
{% else %}
{{ job.state }}
{% end %}
<tr class="success">
{% elif job.state in ("dispatching", "uploading") %}
<tr class="info">
- {% elif job.state in ("aborted", "dependency_error", "failed") %}
+ {% elif job.state in ("aborted", "failed") %}
<tr class="error">
{% else %}
<tr>
--- /dev/null
+{% if isinstance(user, users.User) %}
+ {% if user.is_admin() %}
+ <i class="icon-star"></i>
+ {% else %}
+ <i class="icon-user"></i>
+ {% end %}
+ <a href="/user/{{ user.name }}">
+ {{ user.realname }}
+ </a>
+{% elif user %}
+ {% import email.utils %}
+ {% set name, email_address = email.utils.parseaddr(user) %}
+
+ <i class="icon-envelope"></i>
+ <a href="mailto:{{ email_address }}">{{ name or email_address }}</a>
+{% end %}
\ No newline at end of file
{% block message %}
{% if entry.get_message(current_user) %}
- {% module Text(entry.get_message(current_user), remove_linebreaks=False) %}
+ {% module Text(entry.get_message(current_user)) %}
{% else %}
<p class="muted">
{{ _("No comment given.") }}
{{ format_date(entry.time) }}
</p>
- {% module Text(entry.get_message(current_user), pre=False) %}
+ {% module Text(entry.get_message(current_user)) %}
</div>
</li>
{% end block %}
{% block message %}
- {% module Text(entry.get_message(current_user), pre=False) %}
+ {% module Text(entry.get_message(current_user)) %}
{% end block %}
</div>
{% end block %}
+++ /dev/null
-{% if type == "string" and maintainer %}
- <i class="icon-envelope"></i>
- {% raw format_email(maintainer) %}
-{% elif type == "user" %}
- {% if maintainer.is_admin() %}
- <i class="icon-star"></i>
- {% else %}
- <i class="icon-user"></i>
- {% end %}
- <a href="/user/{{ maintainer.name }}">
- {{ maintainer.realname }}
- </a>
-{% end %}
{% if pkg.maintainer %}
<dt>{{ _("Maintainer") }}</dt>
- <dd>{% module Maintainer(pkg.maintainer) %}</dd>
+ <dd>{% module LinkToUser(pkg.maintainer) %}</dd>
{% end %}
</dl>
--- /dev/null
+{% for paragraph in paragraphs %}
+ <p>
+ {% apply linkify %}
+ {{ paragraph }}
+ {% end %}
+ </p>
+{% end %}
\ No newline at end of file
<h4>
<div class="pull-right">
<div class="btn-group">
- <a class="btn btn-mini" href="{{ bugtracker.enter_url(pkg.name) }}" target="_blank">
+ <a class="btn btn-mini" href="{{ backend.bugzilla.enter_url(pkg.name) }}" target="_blank">
<i class="icon-asterisk"></i> {{ _("File new bug") }}
</a>
- <a class="btn btn-mini" href="{{ bugtracker.buglist_url(pkg.name) }}" target="_blank">
+ <a class="btn btn-mini" href="{{ backend.bugzilla.buglist_url(pkg.name) }}" target="_blank">
{{ _("Show all bugs") }}
</a>
</div>
{% if pkg.maintainer %}
<tr>
<td>{{ _("Maintainer") }}</td>
- <td>{% module Maintainer(pkg.maintainer) %}</td>
+ <td>{% module LinkToUser(pkg.maintainer) %}</td>
</tr>
{% end %}
<tr>
-{% extends "base.html" %}
+{% extends "../base.html" %}
{% block title %}{{ _("Package list") }}{% end block %}
<h1>{{ _("Package list") }}</h1>
</div>
- <div class="row">
- <div class="span6">
- <p>
- {{ _("This is an alphabetically ordered list of all packages in the distribution.") }}
- {{ _("Click on a link to see further information about the package.") }}
- </p>
- </div>
-
- <div class="span6">
- <div class="btn-group">
- <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
- {{ _("Selection") }}
- <span class="caret"></span>
- </a>
- <ul class="dropdown-menu">
- <li>
- <a href="?show=broken">{{ _("Show broken packages") }}</a>
- </li>
- <li>
- <a href="?show=all">{{ _("Show all packages") }}</a>
- </li>
- </ul>
- </div>
- </div>
- </div>
+ <p>
+ {{ _("This is an alphabetically ordered list of all packages in the distribution.") }}
+ {{ _("Click on a link to see further information about the package.") }}
+ </p>
<div class="row">
<div class="span12">
<tr>
<td colspan="2">
<a name="{{ letter }}"></a>
- <h2>{{ letter.upper() }} <small>({{ len(pkgs) }})</small></h2>
+ <h2>{{ letter.upper() }}</h2>
</td>
</tr>
- {% for pkg, summary in pkgs %}
+ {% for pkg in pkgs %}
<tr>
<td>
- <a href="/package/{{ pkg }}">{{ pkg }}</a>
+ <a href="/package/{{ pkg.name }}">{{ pkg.name }}</a>
</td>
<td>
- {{ summary }}
+ {{ pkg.summary }}
</td>
</tr>
{% end %}
--- /dev/null
+{% extends "base.html" %}
+
+{% block title %}{{ _("Job Queue") }} - {{ arch or _("All Architectures") }}{% end block %}
+
+{% block body %}
+ <ul class="breadcrumb">
+ <li>
+ <a href="/">{{ _("Home") }}</a>
+ <span class="divider">/</span>
+ </li>
+ <li {% if not arch %}class="active"{% end %}>
+ <a href="/queue">{{ _("Job Queue") }}</a>
+ {% if arch %}
+ <span class="divider">/</span>
+ {% end %}
+ </li>
+ {% if arch %}
+ <li class="active">
+ <a href="/queue/{{ arch }}">{{ arch }}</a>
+ </li>
+ {% end %}
+ </ul>
+
+ <div class="page-header">
+ <h2>{{ _("Job Queue") }}</h2>
+ </div>
+
+ <ul class="nav nav-pills">
+ <li {% if not arch %}class="active"{% end %}>
+ <a href="/queue">{{ _("All") }}</a>
+ </li>
+
+ {% for a in backend.arches %}
+ <li {% if a == arch %}class="active"{% end %}>
+ <a href="/queue/{{ a }}">{{ a }}</a>
+ </li>
+ {% end %}
+ </ul>
+
+ {% module JobsList(queue) %}
+{% end block %}
<div class="row">
<div class="span8">
<blockquote>
- {% module Text(repo.description, pre=False) %}
+ {% module Text(repo.description) %}
</blockquote>
<br><br>
from ..constants import *
from ..decorators import *
-from .handlers import *
-
-from . import handlers_api
+# Import all handlers
+from . import api
+from . import auth
+from . import builders
+from . import builds
+from . import distributions
from . import errors
+from . import jobs
+from . import keys
from . import mirrors
+from . import packages
+from . import search
+from . import updates
+from . import users
+from .handlers import *
+
from . import ui_modules
# Enable logging
"JobsTable" : ui_modules.JobsTableModule,
"CommentsTable" : ui_modules.CommentsTableModule,
"FilesTable" : ui_modules.FilesTableModule,
+ "LinkToUser" : ui_modules.LinkToUserModule,
"LogTable" : ui_modules.LogTableModule,
"LogFilesTable" : ui_modules.LogFilesTableModule,
- "Maintainer" : ui_modules.MaintainerModule,
"PackagesTable" : ui_modules.PackagesTableModule,
"PackageTable2" : ui_modules.PackageTable2Module,
"PackageHeader" : ui_modules.PackageHeaderModule,
(r"/", IndexHandler),
# Handle all the users logins/logouts/registers and stuff.
- (r"/login", LoginHandler),
- (r"/logout", LogoutHandler),
- (r"/register", RegisterHandler),
- (r"/password-recovery", PasswordRecoveryHandler),
+ (r"/login", auth.LoginHandler),
+ (r"/logout", auth.LogoutHandler),
+ (r"/register", auth.RegisterHandler),
+ (r"/password-recovery", auth.PasswordRecoveryHandler),
# User profiles
- (r"/users", UsersHandler),
- (r"/user/(\w+)/impersonate", UserImpersonateHandler),
- (r"/user/(\w+)/passwd", UserPasswdHandler),
- (r"/user/(\w+)/delete", UserDeleteHandler),
- (r"/user/(\w+)/edit", UserEditHandler),
- (r"/user/(\w+)/activate", ActivationHandler),
- (r"/user/(\w+)", UserHandler),
- (r"/profile", UserHandler),
- (r"/profile/builds", UsersBuildsHandler),
+ (r"/users", users.UsersHandler),
+ (r"/user/(\w+)/impersonate", users.UserImpersonateHandler),
+ (r"/user/(\w+)/passwd", users.UserPasswdHandler),
+ (r"/user/(\w+)/delete", users.UserDeleteHandler),
+ (r"/user/(\w+)/edit", users.UserEditHandler),
+ (r"/user/(\w+)/activate", auth.ActivationHandler),
+ (r"/user/(\w+)", users.UserHandler),
+ (r"/profile", users.UserHandler),
+ (r"/profile/builds", users.UsersBuildsHandler),
# Packages
- (r"/packages", PackageListHandler),
- (r"/package/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", PackageDetailHandler),
- (r"/package/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/download(.*)", PackageFileDownloadHandler),
- (r"/package/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/view(.*)", PackageFileViewHandler),
- (r"/package/([\w\-\+]+)", PackageNameHandler),
- (r"/package/([\w\-\+]+)/builds/scratch", PackageScratchBuildsHandler),
- (r"/package/([\w\-\+]+)/builds/times", PackageBuildsTimesHandler),
- (r"/package/([\w\-\+]+)/changelog", PackageChangelogHandler),
- (r"/package/([\w\-\+]+)/properties", PackagePropertiesHandler),
+ (r"/packages", packages.IndexHandler),
+ (r"/package/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", packages.PackageDetailHandler),
+ (r"/package/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/download(.*)", packages.PackageFileDownloadHandler),
+ (r"/package/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/view(.*)", packages.PackageFileViewHandler),
+ (r"/package/([\w\-\+]+)", packages.PackageNameHandler),
+ (r"/package/([\w\-\+]+)/builds/scratch", packages.PackageScratchBuildsHandler),
+ (r"/package/([\w\-\+]+)/builds/times", packages.PackageBuildsTimesHandler),
+ (r"/package/([\w\-\+]+)/changelog", packages.PackageChangelogHandler),
+ (r"/package/([\w\-\+]+)/properties", packages.PackagePropertiesHandler),
# Files
(r"/file/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", FileDetailHandler),
# Builds
- (r"/builds", BuildsHandler),
- (r"/builds/filter", BuildFilterHandler),
- (r"/builds/queue", BuildQueueHandler),
- (r"/builds/comments", BuildsCommentsHandler),
- (r"/builds/comments/(\w+)", BuildsCommentsHandler),
- (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", BuildDetailHandler),
- (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/bugs", BuildBugsHandler),
- (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/manage", BuildManageHandler),
- (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/comment", BuildDetailCommentHandler),
- (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/priority", BuildPriorityHandler),
- (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/state", BuildStateHandler),
- (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/watch", BuildWatchersAddHandler),
- (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/watchers", BuildWatchersHandler),
- (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/delete", BuildDeleteHandler),
+ (r"/builds", builds.BuildsHandler),
+ (r"/builds/filter", builds.BuildFilterHandler),
+ (r"/builds/queue", builds.BuildQueueHandler),
+ (r"/builds/comments", builds.BuildsCommentsHandler),
+ (r"/builds/comments/(\w+)", builds.BuildsCommentsHandler),
+ (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", builds.BuildDetailHandler),
+ (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/bugs", builds.BuildBugsHandler),
+ (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/manage", builds.BuildManageHandler),
+ (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/comment", builds.BuildDetailCommentHandler),
+ (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/priority", builds.BuildPriorityHandler),
+ (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/state", builds.BuildStateHandler),
+ (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/watch", builds.BuildWatchersAddHandler),
+ (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/watchers", builds.BuildWatchersHandler),
+ (r"/build/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/delete", builds.BuildDeleteHandler),
+
+ (r"/queue", jobs.ShowQueueHandler),
+ (r"/queue/([\w_]+)", jobs.ShowQueueHandler),
# Jobs
- (r"/jobs", JobsIndexHandler),
- (r"/jobs/filter", JobsFilterHandler),
- (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", JobDetailHandler),
- (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/abort", JobAbortHandler),
- (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/buildroot", JobBuildrootHandler),
- (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/schedule", JobScheduleHandler),
+ (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", jobs.JobDetailHandler),
+ (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/abort", jobs.JobAbortHandler),
+ (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/buildroot", jobs.JobBuildrootHandler),
+ (r"/job/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/schedule", jobs.JobScheduleHandler),
# Builders
- (r"/builders", BuilderListHandler),
- (r"/builder/new", BuilderNewHandler),
- (r"/builder/([A-Za-z0-9\-\.]+)/enable", BuilderEnableHander),
- (r"/builder/([A-Za-z0-9\-\.]+)/disable", BuilderDisableHander),
- (r"/builder/([A-Za-z0-9\-\.]+)/delete", BuilderDeleteHandler),
- (r"/builder/([A-Za-z0-9\-\.]+)/edit", BuilderEditHandler),
- (r"/builder/([A-Za-z0-9\-\.]+)/renew", BuilderRenewPassphraseHandler),
- (r"/builder/([A-Za-z0-9\-\.]+)", BuilderDetailHandler),
+ (r"/builders", builders.BuilderListHandler),
+ (r"/builder/new", builders.BuilderNewHandler),
+ (r"/builder/([A-Za-z0-9\-\.]+)/enable", builders.BuilderEnableHander),
+ (r"/builder/([A-Za-z0-9\-\.]+)/disable", builders.BuilderDisableHander),
+ (r"/builder/([A-Za-z0-9\-\.]+)/delete", builders.BuilderDeleteHandler),
+ (r"/builder/([A-Za-z0-9\-\.]+)/edit", builders.BuilderEditHandler),
+ (r"/builder/([A-Za-z0-9\-\.]+)/renew", builders.BuilderRenewPassphraseHandler),
+ (r"/builder/([A-Za-z0-9\-\.]+)", builders.BuilderDetailHandler),
# Distributions
- (r"/distros", DistributionListHandler),
- (r"/distro/([A-Za-z0-9\-\.]+)", DistributionDetailHandler),
+ (r"/distros", distributions.DistributionListHandler),
+ (r"/distro/([A-Za-z0-9\-\.]+)", distributions.DistributionDetailHandler),
# XXX THOSE URLS ARE DEPRECATED
- (r"/distribution/delete/([A-Za-z0-9\-\.]+)", DistributionDetailHandler),
- (r"/distribution/edit/([A-Za-z0-9\-\.]+)", DistributionEditHandler),
+ (r"/distribution/delete/([A-Za-z0-9\-\.]+)", distributions.DistributionDetailHandler),
+ (r"/distribution/edit/([A-Za-z0-9\-\.]+)", distributions.DistributionEditHandler),
(r"/distro/([A-Za-z0-9\-\.]+)/repo/([A-Za-z0-9\-]+)",
RepositoryDetailHandler),
RepositoryEditHandler),
(r"/distro/([A-Za-z0-9\-\.]+)/source/([A-Za-z0-9\-\.]+)",
- DistroSourceDetailHandler),
+ distributions.DistroSourceDetailHandler),
(r"/distro/([A-Za-z0-9\-\.]+)/source/([A-Za-z0-9\-\.]+)/commits",
- DistroSourceCommitsHandler),
+ distributions.DistroSourceCommitsHandler),
(r"/distro/([A-Za-z0-9\-\.]+)/source/([A-Za-z0-9\-\.]+)/([\w]{40})",
- DistroSourceCommitDetailHandler),
+ distributions.DistroSourceCommitDetailHandler),
(r"/distro/([A-Za-z0-9\-\.]+)/source/([A-Za-z0-9\-\.]+)/([\w]{40})/reset",
- DistroSourceCommitResetHandler),
+ distributions.DistroSourceCommitResetHandler),
(r"/distro/([A-Za-z0-9\-\.]+)/update/create",
- DistroUpdateCreateHandler),
+ distributions.DistroUpdateCreateHandler),
(r"/distro/([A-Za-z0-9\-\.]+)/update/(\d+)/(\d+)",
- DistroUpdateDetailHandler),
+ distributions.DistroUpdateDetailHandler),
# Updates
- (r"/updates", UpdatesHandler),
+ (r"/updates", updates.UpdatesHandler),
# Mirrors
(r"/mirrors", mirrors.MirrorListHandler),
(r"/mirror/([\w\-\.]+)", mirrors.MirrorDetailHandler),
# Key management
- (r"/keys", KeysListHandler),
- (r"/key/import", KeysImportHandler),
- (r"/key/([A-Z0-9]+)", KeysDownloadHandler),
- (r"/key/([A-Z0-9]+)/delete", KeysDeleteHandler),
+ (r"/keys", keys.KeysListHandler),
+ (r"/key/import", keys.KeysImportHandler),
+ (r"/key/([A-Z0-9]+)", keys.KeysDownloadHandler),
+ (r"/key/([A-Z0-9]+)/delete", keys.KeysDeleteHandler),
# Documents
(r"/documents", DocsIndexHandler),
(r"/documents/what-is-the-pakfire-build-service", DocsWhatsthisHandler),
# Search
- (r"/search", SearchHandler),
+ (r"/search", search.SearchHandler),
# Uploads
(r"/uploads", UploadsHandler),
(r"/sessions", SessionsHandler),
# API handlers
- (r"/api/packages/autocomplete", handlers_api.ApiPackagesAutocomplete),
+ (r"/api/packages/autocomplete", api.ApiPackagesAutocomplete),
], default_handler_class=errors.Error404Handler, **settings)
logging.info("Successfully initialied application")
from . import base
-class ApiPackagesAutocomplete(base.ApiBaseHandler):
+class ApiPackagesAutocomplete(base.BaseHandler):
def get(self):
query = self.get_argument("q")
if not query:
raise tornado.web.HTTPError(400)
# Query database.
- packages = self.pakfire.packages.autocomplete(query, limit=8)
+ packages = self.backend.packages.autocomplete(query, limit=8)
res = {
"query" : query,
passphrase = self.get_argument("pass", None)
# Log in the user
- user = self.pakfire.users.auth(name, passphrase)
+ user = self.backend.users.auth(name, passphrase)
# If the login was unsuccessful
if not user:
class ActivationHandler(base.BaseHandler):
def get(self, _user):
- user = self.pakfire.users.get_by_name(_user)
+ user = self.backend.users.get_by_name(_user)
if not user:
raise tornado.web.HTTPError(404)
ns = tornado.web.RequestHandler.get_template_namespace(self)
ns.update({
- "bugtracker" : self.pakfire.bugzilla,
+ "backend" : self.backend,
"hostname" : self.request.host,
"format_date" : self.format_date,
"format_size" : misc.format_size,
tb = None
self.render(error_document, status_code=status_code,
- status_message=status_message, exc_info=exc_info, tb=tb, **kwargs)
-
- @property
- def pakfire(self):
- return self.backend
-
- @property
- def mirrors(self):
- return self.pakfire.mirrors
-
-
-class ApiBaseHandler(BaseHandler):
- pass
+ status_message=status_message, exc_info=exc_info, tb=tb, **kwargs)
\ No newline at end of file
import tornado.web
-from .. import builders
-
from . import base
class BuilderListHandler(base.BaseHandler):
class BuilderDetailHandler(base.BaseHandler):
def get(self, hostname):
- builder = self.pakfire.builders.get_by_name(hostname)
+ builder = self.backend.builders.get_by_name(hostname)
# Get running and pending jobs.
- jobs = self.pakfire.jobs.get_active(builder=builder)
- jobs += builder.jobqueue
+ jobs = builder.active_jobs + list(builder.jobqueue)
# Get log.
log = builder.get_history(limit=5)
if not self.current_user.has_perm("maintain_mirrors"):
raise tornado.web.HTTPError(403, "User is not allowed to do this.")
- builder = self.pakfire.builders.get_by_name(hostname)
+ builder = self.backend.builders.get_by_name(hostname)
with self.db.transaction():
builder.description = self.get_argument("description", None)
class BuilderEditHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, hostname):
- builder = self.pakfire.builders.get_by_name(hostname)
+ builder = self.backend.builders.get_by_name(hostname)
if not builder:
raise tornado.web.HTTPError(404, "Builder not found")
@tornado.web.authenticated
def post(self, hostname):
- builder = self.pakfire.builders.get_by_name(hostname)
+ builder = self.backend.builders.get_by_name(hostname)
if not builder:
raise tornado.web.HTTPError(404, "Builder not found: %s" % hostname)
class BuilderRenewPassphraseHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, name):
- builder = self.pakfire.builders.get_by_name(name)
+ builder = self.backend.builders.get_by_name(name)
passphrase = builder.regenerate_passphrase()
class BuilderDeleteHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, name):
- builder = self.pakfire.builders.get_by_name(name)
+ builder = self.backend.builders.get_by_name(name)
if not builder:
raise tornado.web.HTTPError(404, "Builder not found: %s" % name)
@tornado.web.authenticated
def get(self, hostname):
- builder = self.pakfire.builders.get_by_name(hostname)
+ builder = self.backend.builders.get_by_name(hostname)
if not builder:
raise tornado.web.HTTPError(404, "Builder not found: %s" % hostname)
class BuildBaseHandler(base.BaseHandler):
def get_build(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "No such build: %s" % uuid)
class BuildBugsHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "No such build: %s" % uuid)
fixed_bugs = build.get_bugs()
open_bugs = []
- for bug in self.pakfire.bugzilla.get_bugs_from_component(build.pkg.name):
+ for bug in self.backend.bugzilla.get_bugs_from_component(build.pkg.name):
if bug in fixed_bugs:
continue
@tornado.web.authenticated
def post(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "No such build: %s" % uuid)
def get(self, user_name=None):
user = None
if user_name:
- user = self.pakfire.users.get_by_name(user_name)
+ user = self.backend.users.get_by_name(user_name)
limit = self.get_argument("limit", 10)
offset = self.get_argument("offset", 0)
# Try to get one more comment than requested and check if there
# is a next page that it to be shown.
- comments = self.pakfire.builds.get_comments(limit=limit + 1,
+ comments = self.backend.builds.get_comments(limit=limit + 1,
offset=offset, user=user)
# Set markers for next and prev pages.
class BuildStateHandler(base.BaseHandler):
def get(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "No such build: %s" % uuid)
@tornado.web.authenticated
def post(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "No such build: %s" % uuid)
class BuildDetailCommentHandler(base.BaseHandler):
@tornado.web.authenticated
def post(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "Build not found")
class BuildManageHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "Build not found: %s" % uuid)
@tornado.web.authenticated
def post(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "Build not found: %s" % uuid)
class BuildPriorityHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "Build not found")
@tornado.web.authenticated
def post(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "Build not found")
class BuildWatchersHandler(base.BaseHandler):
def get(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "Build not found")
class BuildWatchersAddHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, uuid, error_msg=None):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "Build not found")
watchers = build.get_watchers()
self.render("builds-watchers-add.html", error_msg=error_msg,
- build=build, users=self.pakfire.users, watchers=watchers)
+ build=build, users=self.backend.users, watchers=watchers)
@tornado.web.authenticated
def post(self, uuid):
- build = self.pakfire.builds.get_by_uuid(uuid)
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
raise tornado.web.HTTPError(404, "Build not found")
user_id = self.get_argument("user_id", self.current_user.id)
assert user_id
- user = self.pakfire.users.get_by_id(user_id)
+ user = self.backend.users.get_by_id(user_id)
if not user:
_ = self.locale.translate
error_msg = _("User not found.")
builder = self.get_argument("builder", None)
state = self.get_argument("state", None)
- builds = self.pakfire.builds.get_latest(state=state, builder=builder,
+ builds = self.backend.builds.get_latest(state=state, builder=builder,
limit=25)
self.render("build-list.html", builds=builds)
class BuildFilterHandler(base.BaseHandler):
def get(self):
- builders = self.pakfire.builders.get_all()
- distros = self.pakfire.distros.get_all()
+ builders = self.backend.builders.get_all()
+ distros = self.backend.distros.get_all()
self.render("build-filter.html", builders=builders, distros=distros)
class DistributionDetailHandler(base.BaseHandler):
def get(self, name):
- distro = self.pakfire.distros.get_by_name(name)
+ distro = self.backend.distros.get_by_name(name)
if not distro:
raise tornado.web.HTTPError(404, "Distro not found")
class DistributionEditHandler(base.BaseHandler):
def prepare(self):
- self.sources = self.pakfire.sources.get_all()
+ self.sources = self.backend.sources.get_all()
@tornado.web.authenticated
def get(self, name):
- distro = self.pakfire.distros.get_by_name(name)
+ distro = self.backend.distros.get_by_name(name)
if not distro:
raise tornado.web.HTTPError(404, "Distro not found")
@tornado.web.authenticated
def post(self, name):
- distro = self.pakfire.distros.get_by_name(name)
+ distro = self.backend.distros.get_by_name(name)
if not distro:
raise tornado.web.HTTPError(404, "Distro not found")
class DistroSourceDetailHandler(base.BaseHandler):
def get(self, distro_ident, source_ident):
- distro = self.pakfire.distros.get_by_name(distro_ident)
+ distro = self.backend.distros.get_by_name(distro_ident)
if not distro:
raise tornado.web.HTTPError(404, "Distro not found")
class DistroSourceCommitsHandler(base.BaseHandler):
def get(self, distro_ident, source_ident):
- distro = self.pakfire.distros.get_by_name(distro_ident)
+ distro = self.backend.distros.get_by_name(distro_ident)
if not distro:
raise tornado.web.HTTPError(404, "Distro not found")
class DistroSourceCommitDetailHandler(base.BaseHandler):
def get(self, distro_ident, source_ident, commit_ident):
- distro = self.pakfire.distros.get_by_name(distro_ident)
+ distro = self.backend.distros.get_by_name(distro_ident)
if not distro:
raise tornado.web.HTTPError(404, "Distribution '%s' not found" % distro_ident)
class DistroSourceCommitResetHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, distro_ident, source_ident, commit_ident):
- distro = self.pakfire.distros.get_by_name(distro_ident)
+ distro = self.backend.distros.get_by_name(distro_ident)
if not distro:
raise tornado.web.HTTPError(404, "Distribution '%s' not found" % distro_ident)
class DistroUpdateCreateHandler(base.BaseHandler):
def get(self, distro_ident):
- distro = self.pakfire.distros.get_by_name(distro_ident)
+ distro = self.backend.distros.get_by_name(distro_ident)
if not distro:
raise tornado.web.HTTPError(404, "Distribution '%s' not found" % distro_ident)
# Get all preset builds.
builds = []
for build in self.get_arguments("builds", []):
- build = self.pakfire.builds.get_by_uuid(build)
+ build = self.backend.builds.get_by_uuid(build)
builds.append(build)
builds.sort()
class DistroUpdateDetailHandler(base.BaseHandler):
def get(self, distro_ident, year, num):
- distro = self.pakfire.distros.get_by_name(distro_ident)
+ distro = self.backend.distros.get_by_name(distro_ident)
if not distro:
raise tornado.web.HTTPError(404, "Distribution '%s' not found" % distro_ident)
# XXX currently unused
class SourceListHandler(base.BaseHandler):
def get(self):
- sources = self.pakfire.sources.get_all()
+ sources = self.backend.sources.get_all()
self.render("source-list.html", sources=sources)
class SourceDetailHandler(base.BaseHandler):
def get(self, id):
- source = self.pakfire.sources.get_by_id(id)
+ source = self.backend.sources.get_by_id(id)
self.render("source-detail.html", source=source)
#!/usr/bin/python
-import random
import tornado.web
from . import base
-from .handlers_auth import *
-from .handlers_builds import *
-from .handlers_builders import *
-from .handlers_distro import *
-from .handlers_jobs import *
-from .handlers_keys import *
-from .handlers_packages import *
-from .handlers_search import *
-from .handlers_updates import *
-from .handlers_users import *
-
class IndexHandler(base.BaseHandler):
def get(self):
- jobs = self.pakfire.jobs.get_active()
- jobs += self.pakfire.jobs.get_latest(age="24 hours", limit=5)
+ jobs = []
+
+ # Get all active jobs
+ jobs += self.backend.jobs.get_active()
+
+ # Get some recently finished jobs
+ jobs += self.backend.jobs.get_recently_ended(limit=12)
# Updates
updates = []
active = True
for type in ("stable", "unstable", "testing"):
- u = self.pakfire.updates.get_latest(type=type)
+ u = self.backend.updates.get_latest(type=type)
if u:
updates.append((type, u, active))
active = False
class FileDetailHandler(base.BaseHandler):
def get(self, uuid):
- pkg, file = self.pakfire.packages.get_with_file_by_uuid(uuid)
+ pkg, file = self.backend.packages.get_with_file_by_uuid(uuid)
if not file:
raise tornado.web.HTTPError(404, "File not found")
class LogHandler(base.BaseHandler):
def get(self):
- self.render("log.html", log=self.pakfire.log)
+ self.render("log.html", log=self.backend.log)
class SessionsHandler(base.BaseHandler):
class RepositoryDetailHandler(base.BaseHandler):
def get(self, distro, repo):
- distro = self.pakfire.distros.get_by_name(distro)
+ distro = self.backend.distros.get_by_name(distro)
if not distro:
raise tornado.web.HTTPError(404)
class RepositoryEditHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, distro, repo):
- distro = self.pakfire.distros.get_by_name(distro)
+ distro = self.backend.distros.get_by_name(distro)
if not distro:
raise tornado.web.HTTPError(404)
class RepositoryConfHandler(base.BaseHandler):
def get(self, distro, repo):
- distro = self.pakfire.distros.get_by_name(distro)
+ distro = self.backend.distros.get_by_name(distro)
if not distro:
raise tornado.web.HTTPError(404)
class RepositoryMirrorlistHandler(base.BaseHandler):
def get(self, distro, repo):
- distro = self.pakfire.distros.get_by_name(distro)
+ distro = self.backend.distros.get_by_name(distro)
if not distro:
raise tornado.web.HTTPError(404)
}
mirrors = []
- for mirror in self.mirrors.make_mirrorlist(self.current_address):
+ for mirror in self.backend.mirrors.make_mirrorlist(self.current_address):
mirrors.append({
"url" : "/".join((mirror.url, repo.basepath, arch)),
"location" : mirror.country_code,
action_id = self.get_argument("id")
- action = self.pakfire.repos.get_action_by_id(action_id)
+ action = self.backend.repos.get_action_by_id(action_id)
if not action:
raise tornado.web.HTTPError(400)
from . import base
-class JobsIndexHandler(base.BaseHandler):
- def get(self):
- # Filter for a certain arch.
- arch = self.get_argument("arch", None)
- if not arch or not self.backend.arches.exists(arch):
- raise tornado.web.HTTPError(400, "Architecture does not exist")
-
- # Check if we need to filter for a certain builder.
- builder_name = self.get_argument("builder", None)
- if builder_name:
- builder = self.pakfire.builders.get_by_name(builder_name)
- else:
- builder = None
-
- # Filter for a certain date.
- date = self.get_argument("date", None)
-
- # Get all jobs, that fulfill the criteria.
- jobs = self.pakfire.jobs.get_latest(limit=50, arch=arch, builder=builder,
- date=date)
-
- self.render("jobs-index.html", jobs=jobs, arch=arch, builder=builder,
- date=date)
+class ShowQueueHandler(base.BaseHandler):
+ def get(self, arch=None):
+ if arch:
+ if not self.backend.arches.exists(arch):
+ raise tornado.web.HTTPError(400, "Architecture does not exist")
+ queue = self.backend.jobqueue.for_arches([arch, "noarch"])
+ else:
+ queue = self.backend.jobqueue
-class JobsFilterHandler(base.BaseHandler):
- def get(self):
- builders = self.pakfire.builders.get_all()
-
- self.render("jobs-filter.html", arches=self.backend.arches, builders=builders)
+ self.render("queue.html", arch=arch, queue=queue)
class JobDetailHandler(base.BaseHandler):
def get(self, uuid):
- job = self.pakfire.jobs.get_by_uuid(uuid)
+ job = self.backend.jobs.get_by_uuid(uuid)
if not job:
raise tornado.web.HTTPError(404, "No such job: %s" % job)
class JobBuildrootHandler(base.BaseHandler):
def get(self, uuid):
- job = self.pakfire.jobs.get_by_uuid(uuid)
+ job = self.backend.jobs.get_by_uuid(uuid)
if not job:
raise tornado.web.HTTPError(404, "Job not found: %s" % uuid)
type = self.get_argument("type")
assert type in self.allowed_types
- job = self.pakfire.jobs.get_by_uuid(uuid)
+ job = self.backend.jobs.get_by_uuid(uuid)
if not job:
raise tornado.web.HTTPError(404, "Job not found: %s" % uuid)
type = self.get_argument("type")
assert type in self.allowed_types
- job = self.pakfire.jobs.get_by_uuid(uuid)
+ job = self.backend.jobs.get_by_uuid(uuid)
if not job:
raise tornado.web.HTTPError(404, "Job not found: %s" % uuid)
except TypeError:
offset = 0
- # Submit the build.
- if type == "test":
- job = job.schedule_test(offset)
+ # Is this supposed to be a test job?
+ test = (type == "test")
- elif type == "rebuild":
- job.schedule_rebuild(offset)
+ with self.db.transaction():
+ new_job = job.restart(test=test)
- self.redirect("/job/%s" % job.uuid)
+ self.redirect("/job/%s" % new_job.uuid)
class JobAbortHandler(base.BaseHandler):
def get_job(self, uuid):
- job = self.pakfire.jobs.get_by_uuid(uuid)
+ job = self.backend.jobs.get_by_uuid(uuid)
if not job:
raise tornado.web.HTTPError(404, "Job not found: %s" % uuid)
def post(self):
data = self.get_argument("data")
- key = self.pakfire.keys.create(data)
+ key = self.backend.keys.create(data)
assert key
self.redirect("/keys")
class KeysDeleteHandler(KeysActionHandler):
@tornado.web.authenticated
def get(self, fingerprint):
- key = self.pakfire.keys.get_by_fpr(fingerprint)
+ key = self.backend.keys.get_by_fpr(fingerprint)
if not key:
raise tornado.web.HTTPError(404, "Could not find key: %s" % fingerprint)
class KeysListHandler(base.BaseHandler):
def get(self):
- keys = self.pakfire.keys.get_all()
+ keys = self.backend.keys.get_all()
self.render("keys-list.html", keys=keys)
class KeysDownloadHandler(base.BaseHandler):
def get(self, fingerprint):
- key = self.pakfire.keys.get_by_fpr(fingerprint)
+ key = self.backend.keys.get_by_fpr(fingerprint)
if not key:
raise tornado.web.HTTPError(404, "Could not find key: %s" % fingerprint)
from ..constants import BUFFER_SIZE
-class PackageIDDetailHandler(base.BaseHandler):
- def get(self, id):
- package = self.packages.get_by_id(id)
- if not package:
- return tornado.web.HTTPError(404, "Package not found: %s" % id)
-
- self.render("package-detail.html", package=package)
-
-
-class PackageListHandler(base.BaseHandler):
+class IndexHandler(base.BaseHandler):
def get(self):
+ # Sort all packages in an array like "<first char>" --> [packages, ...]
+ # to print them in a table for each letter of the alphabet.
packages = {}
- show = self.get_argument("show", None)
- if show == "all":
- states = None
- elif show == "obsoletes":
- states = ["obsolete"]
- elif show == "broken":
- states = ["broken"]
- else:
- states = ["building", "stable", "testing"]
+ for pkg in self.backend.packages.get_list():
+ c = pkg.name[0].lower()
- # Get all packages that fulfill the required parameters.
- pkgs = self.pakfire.packages.get_all_names(
- user=self.current_user, states=states)
+ try:
+ packages[c].append(pkg)
+ except KeyError:
+ packages[c] = [pkg]
- # Sort all packages in an array like "<first char>" --> [packages, ...]
- # to print them in a table for each letter of the alphabet.
- for pkg in pkgs:
- c = pkg[0][0].lower()
+ self.render("packages/index.html", packages=packages)
- if not packages.has_key(c):
- packages[c] = []
- packages[c].append(pkg)
+class PackageIDDetailHandler(base.BaseHandler):
+ def get(self, id):
+ package = self.packages.get_by_id(id)
+ if not package:
+ return tornado.web.HTTPError(404, "Package not found: %s" % id)
- self.render("packages-list.html", packages=packages)
+ self.render("package-detail.html", package=package)
class PackageNameHandler(base.BaseHandler):
def get(self, name):
- builds = self.pakfire.builds.get_active_builds(name)
+ builds = self.backend.builds.get_active_builds(name)
if not builds:
raise tornado.web.HTTPError(404, "Package '%s' was not found" % name)
latest_build = builds[0]
# Get the latest bugs from bugzilla.
- bugs = self.pakfire.bugzilla.get_bugs_from_component(name)
+ bugs = self.backend.bugzilla.get_bugs_from_component(name)
self.render("package-detail-list.html", name=name, builds=builds,
latest_build=latest_build, pkg=latest_build.pkg, bugs=bugs)
offset = self.get_argument("offset", 0)
limit = self.get_argument("limit", 10)
- scratch_builds = self.pakfire.builds.get_by_name(name, type="scratch",
+ scratch_builds = self.backend.builds.get_by_name(name, type="scratch",
limit=limit, offset=offset)
if scratch_builds:
latest_build = scratch_builds[0]
else:
- release_builds = self.pakfire.builds.get_by_name(name, type="release", limit=1)
+ release_builds = self.backend.builds.get_by_name(name, type="release", limit=1)
if not release_builds:
raise tornado.web.HTTPError(404, "Could not find any build with this name: %s" % name)
limit = self.get_argument("limit", 10)
try:
limit = int(limit)
+
except ValueError:
limit = 10
# Get one more build than requested to find out if there are more items
# to display (next button).
- builds = self.pakfire.builds.get_changelog(name, limit=limit + 1, offset=offset)
+ builds = self.backend.builds.get_changelog(name, limit=limit + 1, offset=offset)
if len(builds) >= limit:
have_next = True
class PackageDetailHandler(base.BaseHandler):
def get(self, uuid):
- pkg = self.pakfire.packages.get_by_uuid(uuid)
+ pkg = self.backend.packages.get_by_uuid(uuid)
if not pkg:
raise tornado.web.HTTPError(404, "Package not found: %s" % uuid)
@tornado.web.authenticated
def post(self, name, epoch, version, release):
- pkg = self.pakfire.packages.get_by_tuple(name, epoch, version, release)
+ pkg = self.backend.packages.get_by_tuple(name, epoch, version, release)
action = self.get_argument("action", None)
class PackagePropertiesHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, name):
- build = self.pakfire.builds.get_latest_by_name(name)
+ build = self.backend.builds.get_latest_by_name(name)
if not build:
raise tornado.web.HTTPError(404, "Package '%s' was not found" % name)
@tornado.web.authenticated
def post(self, name):
- build = self.pakfire.builds.get_latest_by_name(name)
+ build = self.backend.builds.get_latest_by_name(name)
if not build:
raise tornado.web.HTTPError(404, "Package '%s' was not found" % name)
class PackageFileDownloadHandler(base.BaseHandler):
def get_file(self, pkg_uuid, filename):
# Fetch package.
- pkg = self.pakfire.packages.get_by_uuid(pkg_uuid)
+ pkg = self.backend.packages.get_by_uuid(pkg_uuid)
if not pkg:
raise tornado.web.HTTPError(404, "Package not found: %s" % pkg_uuid)
class PackageBuildsTimesHandler(base.BaseHandler):
def get(self, name):
- latest_build = self.pakfire.builds.get_latest_by_name(name)
+ latest_build = self.backend.builds.get_latest_by_name(name)
# If no build with this name was found, we cannot go on.
if not latest_build:
raise tornado.web.HTTPError(404)
# Get the summary stats.
- build_times_summary = self.pakfire.builds.get_build_times_summary(name)
+ build_times_summary = self.backend.builds.get_build_times_summary(name)
self.render("packages/builds/times.html", pkg=latest_build.pkg,
build_times_summary=build_times_summary)
# Search for a matching object and redirect to it.
# Search in packages.
- pkg = self.pakfire.packages.get_by_uuid(pattern)
+ pkg = self.backend.packages.get_by_uuid(pattern)
if pkg:
self.redirect("/package/%s" % pkg.uuid)
return
# Search in builds.
- build = self.pakfire.builds.get_by_uuid(pattern)
+ build = self.backend.builds.get_by_uuid(pattern)
if build:
self.redirect("/build/%s" % build.uuid)
return
# Search in jobs.
- job = self.pakfire.jobs.get_by_uuid(pattern)
+ job = self.backend.jobs.get_by_uuid(pattern)
if job:
self.redirect("/job/%s" % job.uuid)
return
if pattern.startswith("/"):
# Do a file search.
- files = self.pakfire.packages.search_by_filename(pattern, limit=50)
+ files = self.backend.packages.search_by_filename(pattern, limit=50)
else:
# Make fulltext search in the packages.
- pkgs = self.pakfire.packages.search(pattern, limit=50)
+ pkgs = self.backend.packages.search(pattern, limit=50)
# Search for users.
- users = self.pakfire.users.search(pattern, limit=50)
+ users = self.backend.users.search(pattern, limit=50)
if len(pkgs) == 1 and not files and not users:
pkg = pkgs[0]
from __future__ import division
import datetime
-import itertools
import math
import pytz
import re
-import string
-import tornado.escape
import tornado.web
from .. import users
LINK = """<a href="%s" target="_blank" rel="noopener">%s</a>"""
- def split_paragraphs(self, s):
- for group_seperator, line_iteration in itertools.groupby(s.splitlines(True), key=str.isspace):
- if group_seperator:
- continue
-
- paragraph = "".join(line_iteration)
- yield paragraph.replace("\n", " ")
-
- def render(self, text, pre=False, remove_linebreaks=True):
- if remove_linebreaks:
- text = text.replace("\n", " ")
-
- # Escape the text and create make urls clickable.
- text = tornado.escape.xhtml_escape(text)
- text = tornado.escape.linkify(text, shorten=True,
- extra_params="target=\"_blank\" rel=\"noopener\"")
+ def render(self, text):
+ # Handle empty messages
+ if not text:
+ return ""
# Search for bug ids that need to be linked to bugzilla
text = re.sub(self.BUGZILLA_PATTERN, self._bugzilla_repl, text, re.I|re.U)
# Search for CVE numbers and create hyperlinks.
text = re.sub(self.CVE_PATTERN, self._cve_repl, text, re.I|re.U)
- if pre:
- return "<pre>%s</pre>" % text
-
- return text
+ return self.render_string("modules/text.html", paragraphs=text.split("\n\n"))
def _bugzilla_repl(self, m):
bug_id = m.group(1)
return self.LINK % ("http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s" % m.group(1), m.group(0))
-class CommitMessageModule(TextModule):
+class CommitMessageModule(UIModule):
def render(self, commit):
- s = "h5. %s\n\n" % commit.subject
-
- paragraphs = self.split_paragraphs(commit.message)
- s += "\n\n".join(paragraphs)
-
- return TextModule.render(self, s, remove_linebreaks=False)
+ return self.render_string("modules/commit-message.html", commit=commit)
class ModalModule(UIModule):
classes.append("muted")
icon = "icon-warning-sign"
- elif state == "dependency_error":
- text = _("Dependency problem")
- classes.append("text-warning")
- icon = "icon-random"
-
elif state == "dispatching":
text = _("Dispatching")
classes.append("text-info")
classes.append("text-success")
icon = "icon-ok"
- elif state == "new":
- text = _("New")
- classes.append("muted")
- icon = "icon-asterisk"
-
elif state == "pending":
text = _("Pending")
classes.append("muted")
entry=entry, u=entry.user, show_build=show_build, **args)
-class MaintainerModule(UIModule):
- def render(self, maintainer):
- if isinstance(maintainer, users.User):
- type = "user"
- else:
- type = "string"
-
- return self.render_string("modules/maintainer.html",
- type=type, maintainer=maintainer)
+class LinkToUserModule(UIModule):
+ def render(self, user):
+ return self.render_string("modules/link-to-user.html", user=user, users=users)
class BuildLogModule(UIModule):
#!/usr/bin/python
-import datetime
-
import tornado.locale
import tornado.web
user = self.current_user
if name:
- user = self.pakfire.users.get_by_name(name)
+ user = self.backend.users.get_by_name(name)
if not user:
raise tornado.web.HTTPError(404, "User does not exist: %s" % name)
class UserActionHandler(base.BaseHandler):
def get_user(self, name):
- user = self.pakfire.users.get_by_name(name)
+ user = self.backend.users.get_by_name(name)
if not user:
raise tornado.web.HTTPError(404)
class UserEditHandler(base.BaseHandler):
@tornado.web.authenticated
def get(self, name):
- user = self.pakfire.users.get_by_name(name)
+ user = self.backend.users.get_by_name(name)
if not user:
raise tornado.web.HTTPError(404)
def post(self, name):
_ = self.locale.translate
- user = self.pakfire.users.get_by_name(name)
+ user = self.backend.users.get_by_name(name)
if not user:
raise tornado.web.HTTPError(404)
if name is None:
user = self.current_user
else:
- user = self.pakfire.users.get_by_name(name)
+ user = self.backend.users.get_by_name(name)
if not user:
raise tornado.web.HTTPError(404, "User not found: %s" % name)
# Get a list of the builds this user has built.
- builds = self.pakfire.builds.get_by_user(user)
+ builds = self.backend.builds.get_by_user(user)
self.render("user-profile-builds.html", user=user, builds=builds)