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