]> git.ipfire.org Git - pbs.git/blob - src/web/builds.py
1440f1c6ac9872261b2b30e00fd992bdadbcc93a
[pbs.git] / src / web / builds.py
1 #!/usr/bin/python
2
3 import tornado.web
4
5 from ..errors import NoSuchDistroError
6 from .. import misc
7
8 from . import base
9 from . import ui_modules
10
11 class APIv1IndexHandler(base.APIMixin, base.BaseHandler):
12 # Allow users to create builds
13 allow_users = True
14
15 @tornado.web.authenticated
16 async def post(self):
17 # Fetch the upload
18 upload = self.get_argument_upload("upload_id")
19 if not upload:
20 raise tornado.web.HTTPError(404, "Could not find upload")
21
22 # Check permissions of the upload
23 if not upload.has_perm(self.current_user):
24 raise tornado.web.HTTPError(403, "No permission for using upload %s" % upload)
25
26 # Fetch the repository
27 repo_name = self.get_argument("repo", None)
28
29 # Did the uploader request to disable test builds?
30 disable_test_builds = self.get_argument_bool("disable_test_builds")
31
32 with self.db.transaction():
33 # Import the package
34 try:
35 package = await self.backend.packages.create(upload)
36
37 # If the distribution that is coded into the package could not be found,
38 # we will send that error to the user...
39 except NoSuchDistroError as e:
40 raise tornado.web.HTTPError(404, "Could not find distribution: %s" % e)
41
42 try:
43 # Find the repository
44 repo = self.current_user.get_repo(package.distro, repo_name)
45 if not repo:
46 raise tornado.web.HTTPError(404, "Could not find repository")
47
48 # Create a new build
49 build = await self.backend.builds.create(repo, package,
50 owner=self.current_user, disable_test_builds=disable_test_builds)
51
52 # If anything goes wrong, we will try to delete the package again
53 except Exception as e:
54 await package.delete(user=self.current_user)
55
56 raise e
57
58 # Delete the upload
59 await upload.delete()
60
61 # Send some data about the build
62 self.finish({
63 "uuid" : build.uuid,
64 "name" : "%s" % build,
65 })
66
67 # Launch all jobs (in the background)
68 self.backend.run_task(self.backend.builds.launch, [build])
69
70
71 class IndexHandler(base.BaseHandler):
72 def get(self):
73 # Pagination
74 offset = self.get_argument_int("offset", None) or 0
75 limit = self.get_argument_int("limit", None) or 25
76
77 # Filters
78 name = self.get_argument("name", None)
79 user = self.get_argument_user("user", None)
80
81 # Fetch the most recent builds
82 if user:
83 builds = user.get_builds(name, limit=limit, offset=offset)
84 else:
85 builds = self.backend.builds.get_recent(name=name, limit=limit, offset=offset)
86
87 # Group builds by date
88 builds = misc.group(builds, lambda build: build.created_at.date())
89
90 self.render("builds/index.html", builds=builds, name=name, user=user,
91 limit=limit, offset=offset)
92
93
94 class ShowHandler(base.BaseHandler):
95 async def get(self, uuid):
96 build = self.backend.builds.get_by_uuid(uuid)
97 if not build:
98 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
99
100 # Fetch any fixed Bugs
101 bugs = await build.get_bugs()
102
103 self.render("builds/show.html", build=build, pkg=build.pkg,
104 distro=build.distro, bugs=bugs)
105
106
107 class CloneHandler(base.BaseHandler):
108 @tornado.web.authenticated
109 def get(self, uuid):
110 build = self.backend.builds.get_by_uuid(uuid)
111 if not build:
112 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
113
114 # Fetch repositories
115 try:
116 repos = self.current_user.repos[build.distro]
117 except KeyError:
118 repos = []
119
120 self.render("builds/clone.html", build=build, repos=repos)
121
122 @tornado.web.authenticated
123 async def post(self, uuid):
124 build = self.backend.builds.get_by_uuid(uuid)
125 if not build:
126 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
127
128 # Fetch the repository
129 repo = self.current_user.get_repo(build.distro, self.get_argument("repo"))
130
131 # Clone the build
132 with self.db.transaction():
133 clone = await self.backend.builds.create(
134 repo=repo, package=build.pkg, owner=self.current_user,
135 )
136
137 # Launch all jobs (in the background)
138 self.backend.run_task(self.backend.builds.launch, [clone])
139
140 self.redirect("/builds/%s" % clone.uuid)
141
142
143 class DeleteHandler(base.BaseHandler):
144 @tornado.web.authenticated
145 def get(self, uuid):
146 build = self.backend.builds.get_by_uuid(uuid)
147 if not build:
148 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
149
150 # Check permissions
151 if not build.can_be_deleted(self.current_user):
152 raise tornado.web.HTTPError(403, "%s cannot delete build %s" \
153 % (self.current_user, build))
154
155 self.render("builds/delete.html", build=build)
156
157 @tornado.web.authenticated
158 async def post(self, uuid):
159 build = self.backend.builds.get_by_uuid(uuid)
160 if not build:
161 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
162
163 # Check permissions
164 if not build.can_be_deleted(self.current_user):
165 raise tornado.web.HTTPError(403, "%s cannot delete build %s" \
166 % (self.current_user, build))
167
168 # Perform Deletion
169 with self.db.transaction():
170 await build.delete(self.current_user)
171
172 self.redirect("/builds")
173
174
175 class WatchHandler(base.BaseHandler):
176 @tornado.web.authenticated
177 def post(self, uuid):
178 build = self.backend.builds.get_by_uuid(uuid)
179 if not build:
180 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
181
182 with self.db.transaction():
183 build.add_watcher(self.current_user)
184
185 self.redirect("/builds/%s" % build.uuid)
186
187
188 class UnwatchHandler(base.BaseHandler):
189 @tornado.web.authenticated
190 def post(self, uuid):
191 build = self.backend.builds.get_by_uuid(uuid)
192 if not build:
193 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
194
195 with self.db.transaction():
196 build.remove_watcher(self.current_user)
197
198 self.redirect("/builds/%s" % build.uuid)
199
200
201 class CommentHandler(base.BaseHandler):
202 @tornado.web.authenticated
203 async def post(self, uuid):
204 build = self.backend.builds.get_by_uuid(uuid)
205 if not build:
206 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
207
208 text = self.get_argument("text")
209
210 # Add a new comment to the build
211 with self.db.transaction():
212 await build.comment(self.current_user, text)
213
214 # Redirect to the build
215 self.redirect("/builds/%s" % build.uuid)
216
217
218 class BugHandler(base.BaseHandler):
219 @tornado.web.authenticated
220 async def get(self, uuid):
221 build = self.backend.builds.get_by_uuid(uuid)
222 if not build:
223 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
224
225 # Fetch fields
226 fields = await self.backend.bugzilla.fields
227
228 self.render("builds/bug.html", build=build, fields=fields)
229
230 @tornado.web.authenticated
231 async def post(self, uuid):
232 build = self.backend.builds.get_by_uuid(uuid)
233 if not build:
234 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
235
236 # Is the user connected to Bugzilla?
237 if not self.current_user.bugzilla:
238 raise tornado.web.HTTPError(400, "%s is not connected to Bugzilla" \
239 % self.current_user)
240
241 kwargs = {
242 # Summary & Description
243 "summary" : self.get_argument("summary"),
244 "description" : self.get_argument("description", None),
245 } | build.bugzilla_fields
246
247 # Create the bug
248 bug = await self.current_user.bugzilla.create_bug(**kwargs)
249
250 # Send the attachments
251 for job in build.jobs:
252 if not self.get_argument_bool("attach_log_%s" % job.uuid):
253 continue
254
255 # Open the logfile
256 try:
257 log = await job.open_log()
258 except FileNotFoundError as e:
259 log.warning("Could not open log file for %s" % job)
260 continue
261
262 # Attach it to the bug
263 await bug.attach(summary="Log file for %s" % job, filename="%s.log" % job,
264 data=log, content_type="text/plain")
265
266 self.render("builds/bug-created.html", build=build, bug=bug)
267
268
269 class ReposAddHandler(base.BaseHandler):
270 @tornado.web.authenticated
271 def get(self, uuid):
272 build = self.backend.builds.get_by_uuid(uuid)
273 if not build:
274 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
275
276 # Fetch all available repositories
277 try:
278 repos = self.current_user.repos[build.distro]
279 except KeyError:
280 repos = None
281
282 self.render("builds/repos/add.html", build=build, repos=repos)
283
284 @tornado.web.authenticated
285 async def post(self, uuid):
286 build = self.backend.builds.get_by_uuid(uuid)
287 if not build:
288 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
289
290 slug = self.get_argument("repo")
291
292 # Fetch the repository
293 repo = self.current_user.get_repo(build.distro, slug)
294 if not repo:
295 raise tornado.web.HTTPError(400, "Could not find repository '%s'" % slug)
296
297 # Add the build to the repository
298 with self.db.transaction():
299 await repo.add_build(build, user=self.current_user)
300
301 self.redirect("/builds/%s" % build.uuid)
302
303
304 class ReposRemoveHandler(base.BaseHandler):
305 @tornado.web.authenticated
306 def get(self, uuid):
307 build = self.backend.builds.get_by_uuid(uuid)
308 if not build:
309 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
310
311 # Raise error when the build is in to repositories
312 if not build.repos:
313 raise tornado.web.HTTPError(400)
314
315 self.render("builds/repos/remove.html", build=build)
316
317 @tornado.web.authenticated
318 async def post(self, uuid):
319 build = self.backend.builds.get_by_uuid(uuid)
320 if not build:
321 raise tornado.web.HTTPError(404, "Could not find build %s" % uuid)
322
323 # Fetch all selected repos
324 repos = self.get_arguments("repo")
325
326 # Raise an error if nothing has been selected
327 if not repos:
328 raise tornado.web.HTTPError(400, "No repositories selected")
329
330 # Find all selected repositories
331 repos = [repo for repo in build.repos if repo.slug in repos]
332
333 # Remove build from all repositories
334 with self.db.transaction():
335 for repo in repos:
336 await repo.remove_build(build, user=self.current_user)
337
338 self.redirect("/builds/%s" % build.uuid)
339
340
341 class GroupShowHandler(base.BaseHandler):
342 def get(self, uuid):
343 group = self.backend.builds.groups.get_by_uuid(uuid)
344 if not group:
345 raise tornado.web.HTTPError(404, "Could not find build group %s" % uuid)
346
347 self.render("builds/groups/show.html", group=group)
348
349
350 class ListModule(ui_modules.UIModule):
351 def render(self, builds, limit=None, shorter=False, more_url=None):
352 rest = None
353
354 # Limit builds
355 if limit:
356 builds, rest = builds[:limit], builds[limit:]
357
358 return self.render_string("builds/modules/list.html", builds=builds,
359 rest=rest, shorter=shorter, more_url=more_url)
360
361
362 class GroupListModule(ui_modules.UIModule):
363 def render(self, group, limit=None):
364 return self.render_string("builds/groups/modules/list.html",
365 group=group, limit=limit)
366
367
368 class WatchersModule(ui_modules.UIModule):
369 def render(self, build, watchers=None):
370 if watchers is None:
371 watchers = build.watchers
372
373 return self.render_string("builds/modules/watchers.html",
374 build=build, watchers=watchers)