import tornado.web
+from ..errors import NoSuchDistroError
+from .. import misc
+
from . import base
from . import ui_modules
-class BuildBaseHandler(base.BaseHandler):
- def get_build(self, uuid):
- build = self.backend.builds.get_by_uuid(uuid)
- if not build:
- raise tornado.web.HTTPError(404, "No such build: %s" % uuid)
+class APIv1IndexHandler(base.APIMixin, base.BaseHandler):
+ # Allow users to create builds
+ allow_users = True
- return build
+ @tornado.web.authenticated
+ async def post(self):
+ # Fetch the upload
+ upload = self.get_argument_upload("upload_id")
+ if not upload:
+ raise tornado.web.HTTPError(404, "Could not find upload")
+ # Check permissions of the upload
+ if not upload.has_perm(self.current_user):
+ raise tornado.web.HTTPError(403, "No permission for using upload %s" % upload)
-class IndexHandler(base.BaseHandler):
- def get(self):
- # Fetch the most recent builds
- builds = self.backend.builds.get_recent(limit=25)
+ # Fetch the repository
+ repo_name = self.get_argument("repo", None)
- self.render("builds/index.html", builds=builds)
+ # Did the uploader request to disable test builds?
+ disable_test_builds = self.get_argument_bool("disable_test_builds")
+ with self.db.transaction():
+ # Import the package
+ try:
+ package = await self.backend.packages.create(upload)
-class ShowHandler(BuildBaseHandler):
- def get(self, uuid):
- build = self.backend.builds.get_by_uuid(uuid)
- if not build:
- raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
+ # If the distribution that is coded into the package could not be found,
+ # we will send that error to the user...
+ except NoSuchDistroError as e:
+ raise tornado.web.HTTPError(404, "Could not find distribution: %s" % e)
- # Cache the log.
- log = build.get_log()
+ # Find the repository
+ repo = self.current_user.get_repo(package.distro, repo_name)
+ if not repo:
+ raise tornado.web.HTTPError(404, "Could not find repository")
- # Bugs.
- bugs = build.get_bugs()
+ # Create a new build
+ build = await self.backend.builds.create(repo, package,
+ owner=self.current_user, disable_test_builds=disable_test_builds)
- self.render("builds/show.html", build=build, log=log, pkg=build.pkg,
- distro=build.distro, bugs=bugs, repo=build.repo)
+ # Delete the upload
+ await upload.delete()
+ # Send some data about the build
+ self.finish({
+ "uuid" : build.uuid,
+ "name" : "%s" % build,
+ })
-class BuildDeleteHandler(BuildBaseHandler):
- @tornado.web.authenticated
- def get(self, uuid):
- build = self.get_build(uuid)
+ # Launch all jobs (in the background)
+ self.backend.run_task(self.backend.builds.launch, [build])
- # Check if the user has got sufficient rights to do this modification.
- if not build.has_perm(self.current_user):
- raise tornado.web.HTTPError(403)
- # Check if the user confirmed the action.
- confirmed = self.get_argument("confirmed", None)
- if confirmed:
- # Save the name of the package to redirect the user
- # to the other packages of that name.
- package_name = build.pkg.name
+class IndexHandler(base.BaseHandler):
+ def get(self):
+ # Pagination
+ offset = self.get_argument_int("offset", None) or 0
+ limit = self.get_argument_int("limit", None) or 25
- # Delete the build and everything that comes with it.
- with self.db.transaction():
- build.delete()
+ # Filters
+ name = self.get_argument("name", None)
+ user = self.get_argument_user("user", None)
- return self.redirect("/package/%s" % package_name)
+ # Fetch the most recent builds
+ if user:
+ builds = user.get_builds(name, limit=limit, offset=offset)
+ else:
+ builds = self.backend.builds.get_recent(name=name, limit=limit, offset=offset)
- self.render("build-delete.html", build=build)
+ # Group builds by date
+ builds = misc.group(builds, lambda build: build.created_at.date())
+ self.render("builds/index.html", builds=builds, name=name, user=user,
+ limit=limit, offset=offset)
-class BuildBugsHandler(base.BaseHandler):
- @tornado.web.authenticated
- def get(self, uuid):
- build = self.backend.builds.get_by_uuid(uuid)
- if not build:
- raise tornado.web.HTTPError(404, "No such build: %s" % uuid)
- # Check if the user has got the right to alter this build.
- if not build.has_perm(self.current_user):
- raise tornado.web.HTTPError(403)
+class QueueHandler(base.BaseHandler):
+ def get(self):
+ self.render("builds/queue.html", queue=self.backend.jobs.queue)
- # Bugs.
- fixed_bugs = build.get_bugs()
- open_bugs = []
- for bug in self.backend.bugzilla.get_bugs_from_component(build.pkg.name):
- if bug in fixed_bugs:
- continue
+class ShowHandler(base.BaseHandler):
+ async def get(self, uuid):
+ build = self.backend.builds.get_by_uuid(uuid)
+ if not build:
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
+
+ # Fetch any fixed Bugs
+ bugs = await build.get_bugs()
- open_bugs.append(bug)
+ self.render("builds/show.html", build=build, pkg=build.pkg,
+ distro=build.distro, bugs=bugs)
- self.render("build-bugs.html", build=build, pkg=build.pkg,
- fixed_bugs=fixed_bugs, open_bugs=open_bugs)
+class CloneHandler(base.BaseHandler):
@tornado.web.authenticated
- def post(self, uuid):
+ def get(self, uuid):
build = self.backend.builds.get_by_uuid(uuid)
if not build:
- raise tornado.web.HTTPError(404, "No such build: %s" % uuid)
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- # Check if the user has got the right to alter this build.
- if not build.has_perm(self.current_user):
- raise tornado.web.HTTPError(403)
+ # Fetch repositories
+ try:
+ repos = self.current_user.repos[build.distro]
+ except KeyError:
+ repos = []
- action = self.get_argument("action", None)
- bugid = self.get_argument("bugid")
+ self.render("builds/clone.html", build=build, repos=repos)
- # Convert the bug id to integer.
- try:
- bugid = int(bugid)
- except ValueError:
- raise tornado.web.HTTPError(400, "Bad bug id given: %s" % bugid)
+ @tornado.web.authenticated
+ async def post(self, uuid):
+ build = self.backend.builds.get_by_uuid(uuid)
+ if not build:
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- if action == "add":
- # Add bug to the build.
- build.add_bug(bugid, user=self.current_user)
+ # Fetch the repository
+ repo = self.current_user.get_repo(build.distro, self.get_argument("repo"))
- elif action == "remove":
- # Remove bug from the build.
- build.rem_bug(bugid, user=self.current_user)
+ # Clone the build
+ with self.db.transaction():
+ clone = await self.backend.builds.create(
+ repo=repo, package=build.pkg, owner=self.current_user,
+ )
- else:
- raise tornado.web.HTTPError(400, "Unhandled action: %s" % action)
+ # Launch all jobs (in the background)
+ self.backend.run_task(self.backend.builds.launch, [clone])
- self.redirect("/build/%s/bugs" % build.uuid)
+ self.redirect("/builds/%s" % clone.uuid)
-class BuildStateHandler(base.BaseHandler):
+class DeleteHandler(base.BaseHandler):
+ @tornado.web.authenticated
def get(self, uuid):
build = self.backend.builds.get_by_uuid(uuid)
if not build:
- raise tornado.web.HTTPError(404, "No such build: %s" % uuid)
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- self.render("build-state.html", build=build)
+ # Check permissions
+ if not build.can_be_deleted(self.current_user):
+ raise tornado.web.HTTPError(403, "%s cannot delete build %s" \
+ % (self.current_user, build))
+
+ self.render("builds/delete.html", build=build)
@tornado.web.authenticated
- def post(self, uuid):
+ async def post(self, uuid):
build = self.backend.builds.get_by_uuid(uuid)
if not build:
- raise tornado.web.HTTPError(404, "No such build: %s" % uuid)
-
- # Check if user has the right to perform this action.
- if not build.has_perm(self.current_user):
- raise tornado.web.HTTPError(403, "User is not allowed to perform this action")
-
- # Check if given state is valid.
- state = self.get_argument("state", None)
- if not state in ("broken", "unbreak", "obsolete"):
- raise tornado.web.HTTPError(400, "Invalid argument given: %s" % state)
-
- # XXX this is not quite accurate
- if state == "unbreak":
- state = "stable"
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- rem_from_repo = self.get_argument("rem_from_repo", False)
- if rem_from_repo == "on":
- rem_from_repo = True
+ # Check permissions
+ if not build.can_be_deleted(self.current_user):
+ raise tornado.web.HTTPError(403, "%s cannot delete build %s" \
+ % (self.current_user, build))
- # Perform the state change.
- build.update_state(state, user=self.current_user, remove=rem_from_repo)
+ # Perform Deletion
+ with self.db.transaction():
+ await build.delete(self.current_user)
- self.redirect("/build/%s" % build.uuid)
+ self.redirect("/builds")
-class BuildDetailCommentHandler(base.BaseHandler):
+class WatchHandler(base.BaseHandler):
@tornado.web.authenticated
def post(self, uuid):
build = self.backend.builds.get_by_uuid(uuid)
-
if not build:
- raise tornado.web.HTTPError(404, "Build not found")
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- vote = self.get_argument("vote", "none")
+ with self.db.transaction():
+ build.add_watcher(self.current_user)
+
+ self.redirect("/builds/%s" % build.uuid)
- if vote == "up":
- vote = 1
- elif vote == "down":
- vote = -1
- else:
- vote = 0
- text = self.get_argument("text", "")
+class UnwatchHandler(base.BaseHandler):
+ @tornado.web.authenticated
+ def post(self, uuid):
+ build = self.backend.builds.get_by_uuid(uuid)
+ if not build:
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- # Add a new comment to the build.
- if text or vote:
- build.add_comment(self.current_user, text, vote)
+ with self.db.transaction():
+ build.remove_watcher(self.current_user)
- # Redirect to the build detail page.
- self.redirect("/build/%s" % build.uuid)
+ self.redirect("/builds/%s" % build.uuid)
-class BuildManageHandler(base.BaseHandler):
+class CommentHandler(base.BaseHandler):
@tornado.web.authenticated
- def get(self, uuid):
+ async def post(self, uuid):
build = self.backend.builds.get_by_uuid(uuid)
if not build:
- raise tornado.web.HTTPError(404, "Build not found: %s" % uuid)
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- mode = "user"
- if self.current_user.is_admin():
- mode = self.get_argument("mode", "user")
+ text = self.get_argument("text")
- # Get the next repo.
- if build.repo:
- next_repo = build.repo.__next__
- else:
- next_repo = build.distro.first_repo
+ # Add a new comment to the build
+ with self.db.transaction():
+ await build.comment(self.current_user, text)
+
+ # Redirect to the build
+ self.redirect("/builds/%s" % build.uuid)
- self.render("build-manage.html", mode=mode, build=build,
- distro=build.distro, repo=build.repo, next_repo=next_repo)
+class BugHandler(base.BaseHandler):
@tornado.web.authenticated
- def post(self, uuid):
+ async def get(self, uuid):
build = self.backend.builds.get_by_uuid(uuid)
if not build:
- raise tornado.web.HTTPError(404, "Build not found: %s" % uuid)
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- # check for sufficient permissions
- if not build.has_perm(self.current_user):
- raise tornado.web.HTTPError(403)
+ # Fetch fields
+ fields = await self.backend.bugzilla.fields
- action = self.get_argument("action")
- assert action in ("push", "unpush")
+ self.render("builds/bug.html", build=build, fields=fields)
- current_repo = build.repo
+ @tornado.web.authenticated
+ async def post(self, uuid):
+ build = self.backend.builds.get_by_uuid(uuid)
+ if not build:
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- if action == "unpush":
- current_repo.rem_build(build, user=self.current_user)
+ # Is the user connected to Bugzilla?
+ if not self.current_user.bugzilla:
+ raise tornado.web.HTTPError(400, "%s is not connected to Bugzilla" \
+ % self.current_user)
- elif action == "push":
- repo_name = self.get_argument("repo")
- next_repo = build.distro.get_repo(repo_name)
+ kwargs = {
+ # Summary & Description
+ "summary" : self.get_argument("summary"),
+ "description" : self.get_argument("description", None),
+ } | build.bugzilla_fields
- if not next_repo:
- raise tornado.web.HTTPError(404, "No such repository: %s" % next_repo)
+ # Create the bug
+ bug = await self.current_user.bugzilla.create_bug(**kwargs)
- if not self.current_user.is_admin():
- if not distro.repo.__next__ == next_repo:
- raise tornado.web.HTTPError(403)
+ # Send the attachments
+ for job in build.jobs:
+ if not self.get_argument_bool("attach_log_%s" % job.uuid):
+ continue
- if current_repo:
- current_repo.move_build(build, next_repo, user=self.current_user)
- else:
- next_repo.add_build(build, user=self.current_user)
+ # Open the logfile
+ try:
+ log = await job.open_log()
+ except FileNotFoundError as e:
+ log.warning("Could not open log file for %s" % job)
+ continue
+
+ # Attach it to the bug
+ await bug.attach(summary="Log file for %s" % job, filename="%s.log" % job,
+ data=log, content_type="text/plain")
- self.redirect("/build/%s" % build.uuid)
+ self.render("builds/bug-created.html", build=build, bug=bug)
-class BuildWatchersHandler(base.BaseHandler):
+class ReposAddHandler(base.BaseHandler):
+ @tornado.web.authenticated
def get(self, uuid):
build = self.backend.builds.get_by_uuid(uuid)
+ if not build:
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
+ # Fetch all available repositories
+ try:
+ repos = self.current_user.repos[build.distro]
+ except KeyError:
+ repos = None
+
+ self.render("builds/repos/add.html", build=build, repos=repos)
+
+ @tornado.web.authenticated
+ async def post(self, uuid):
+ build = self.backend.builds.get_by_uuid(uuid)
if not build:
- raise tornado.web.HTTPError(404, "Build not found")
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- # Get a list of all watchers and sort them by their realname.
- watchers = build.get_watchers()
- watchers.sort(key=lambda watcher: watcher.realname)
+ slug = self.get_argument("repo")
- self.render("builds-watchers-list.html", build=build, watchers=watchers)
+ # Fetch the repository
+ repo = self.current_user.get_repo(build.distro, slug)
+ if not repo:
+ raise tornado.web.HTTPError(400, "Could not find repository '%s'" % slug)
+ # Add the build to the repository
+ with self.db.transaction():
+ await repo.add_build(build, user=self.current_user)
-class BuildWatchersAddHandler(base.BaseHandler):
+ self.redirect("/builds/%s" % build.uuid)
+
+
+class ReposRemoveHandler(base.BaseHandler):
@tornado.web.authenticated
- def get(self, uuid, error_msg=None):
+ def get(self, uuid):
build = self.backend.builds.get_by_uuid(uuid)
-
if not build:
- raise tornado.web.HTTPError(404, "Build not found")
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- # Get a list of all users that are currently watching this build.
- watchers = build.get_watchers()
+ # Raise error when the build is in to repositories
+ if not build.repos:
+ raise tornado.web.HTTPError(400)
- self.render("builds-watchers-add.html", error_msg=error_msg,
- build=build, users=self.backend.users, watchers=watchers)
+ self.render("builds/repos/remove.html", build=build)
@tornado.web.authenticated
- def post(self, uuid):
+ async def post(self, uuid):
build = self.backend.builds.get_by_uuid(uuid)
-
if not build:
- raise tornado.web.HTTPError(404, "Build not found")
+ raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
- # Get the user id of the new watcher.
- user_id = self.current_user.id
+ # Fetch all selected repos
+ repos = self.get_arguments("repo")
- if self.current_user.is_admin():
- user_id = self.get_argument("user_id", self.current_user.id)
- assert user_id
+ # Raise an error if nothing has been selected
+ if not repos:
+ raise tornado.web.HTTPError(400, "No repositories selected")
- user = self.backend.users.get_by_id(user_id)
- if not user:
- _ = self.locale.translate
- error_msg = _("User not found.")
+ # Find all selected repositories
+ repos = [repo for repo in build.repos if repo.slug in repos]
- return self.get(uuid, error_msg=error_msg)
+ # Remove build from all repositories
+ with self.db.transaction():
+ for repo in repos:
+ await repo.remove_build(build, user=self.current_user)
- # Actually add the user to the list of watchers.
- build.add_watcher(user)
+ self.redirect("/builds/%s" % build.uuid)
- # Send user back to the build detail page.
- self.redirect("/build/%s" % build.uuid)
+class GroupShowHandler(base.BaseHandler):
+ def get(self, uuid):
+ group = self.backend.builds.groups.get_by_uuid(uuid)
+ if not group:
+ raise tornado.web.HTTPError(404, "Could not find build group %s" % uuid)
-class BuildListHandler(base.BaseHandler):
- def get(self):
- builder = self.get_argument("builder", None)
- state = self.get_argument("state", None)
+ self.render("builds/groups/show.html", group=group)
- builds = self.backend.builds.get_latest(state=state, builder=builder,
- limit=25)
- self.render("build-list.html", builds=builds)
+class ListModule(ui_modules.UIModule):
+ def render(self, builds, limit=None, more_url=None):
+ rest = None
+ # Limit builds
+ if limit:
+ builds, rest = builds[:limit], builds[limit:]
-class ListModule(ui_modules.UIModule):
- def render(self, builds):
- return self.render_string("builds/modules/list.html", builds=builds)
+ return self.render_string("builds/modules/list.html", builds=builds,
+ rest=rest, more_url=more_url)
+
+
+class GroupListModule(ui_modules.UIModule):
+ def render(self, group, limit=None):
+ return self.render_string("builds/groups/modules/list.html",
+ group=group, limit=limit)
+
+
+class WatchersModule(ui_modules.UIModule):
+ def render(self, build, watchers=None):
+ if watchers is None:
+ watchers = build.watchers
+
+ return self.render_string("builds/modules/watchers.html",
+ build=build, watchers=watchers)