Package coprs :: Package views :: Package coprs_ns :: Module coprs_general
[hide private]
[frames] | no frames]

Source Code for Module coprs.views.coprs_ns.coprs_general

   1  # coding: utf-8 
   2   
   3  import os 
   4  import time 
   5  import fnmatch 
   6  import re 
   7  import uuid 
   8  import subprocess 
   9  from six.moves.urllib.parse import urljoin 
  10   
  11  import flask 
  12  from flask import render_template, url_for, stream_with_context 
  13  import platform 
  14  import smtplib 
  15  import sqlalchemy 
  16  import modulemd 
  17  from email.mime.text import MIMEText 
  18  from itertools import groupby 
  19   
  20  from pygments import highlight 
  21  from pygments.lexers import get_lexer_by_name 
  22  from pygments.formatters import HtmlFormatter 
  23   
  24  from coprs import app 
  25  from coprs import db 
  26  from coprs import rcp 
  27  from coprs import exceptions 
  28  from coprs import forms 
  29  from coprs import helpers 
  30  from coprs import models 
  31  from coprs.exceptions import ObjectNotFound 
  32  from coprs.logic.coprs_logic import CoprsLogic 
  33  from coprs.logic.packages_logic import PackagesLogic 
  34  from coprs.logic.stat_logic import CounterStatLogic 
  35  from coprs.logic.users_logic import UsersLogic 
  36  from coprs.logic.modules_logic import ModulesLogic 
  37  from coprs.rmodels import TimedStatEvents 
  38   
  39  from coprs.logic.complex_logic import ComplexLogic 
  40   
  41  from coprs.views.misc import login_required, page_not_found, req_with_copr, req_with_copr, generic_error 
  42   
  43  from coprs.views.coprs_ns import coprs_ns 
  44  from coprs.views.groups_ns import groups_ns 
  45   
  46  from coprs.logic import builds_logic, coprs_logic, actions_logic, users_logic 
  47  from coprs.helpers import parse_package_name, generate_repo_url, CHROOT_RPMS_DL_STAT_FMT, CHROOT_REPO_MD_DL_STAT_FMT, \ 
  48      str2bool, url_for_copr_view 
49 50 51 -def url_for_copr_details(copr):
52 return url_for_copr_view( 53 "coprs_ns.copr_detail", 54 "coprs_ns.group_copr_detail", 55 copr)
56
57 58 -def url_for_copr_edit(copr):
59 return url_for_copr_view( 60 "coprs_ns.copr_edit", 61 "coprs_ns.group_copr_edit", 62 copr)
63
64 65 @coprs_ns.route("/", defaults={"page": 1}) 66 @coprs_ns.route("/<int:page>/") 67 -def coprs_show(page=1):
68 query = CoprsLogic.get_multiple(include_unlisted_on_hp=False) 69 query = CoprsLogic.set_query_order(query, desc=True) 70 71 paginator = helpers.Paginator(query, query.count(), page) 72 73 coprs = paginator.sliced_query 74 75 # flask.g.user is none when no user is logged - showing builds from everyone 76 # TODO: builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 5) takes too much time, optimize sql 77 # users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 5) 78 users_builds = builds_logic.BuildsLogic.get_recent_tasks(None, 5) 79 80 return flask.render_template("coprs/show/all.html", 81 coprs=coprs, 82 paginator=paginator, 83 tasks_info=ComplexLogic.get_queues_size(), 84 users_builds=users_builds)
85
86 87 @coprs_ns.route("/<username>/", defaults={"page": 1}) 88 @coprs_ns.route("/<username>/<int:page>/") 89 -def coprs_by_user(username=None, page=1):
90 user = users_logic.UsersLogic.get(username).first() 91 if not user: 92 return page_not_found( 93 "User {0} does not exist.".format(username)) 94 95 query = CoprsLogic.get_multiple_owned_by_username(username) 96 query = CoprsLogic.filter_without_group_projects(query) 97 query = CoprsLogic.set_query_order(query, desc=True) 98 99 paginator = helpers.Paginator(query, query.count(), page) 100 101 coprs = paginator.sliced_query 102 103 # flask.g.user is none when no user is logged - showing builds from everyone 104 users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 5) 105 106 return flask.render_template("coprs/show/user.html", 107 user=user, 108 coprs=coprs, 109 paginator=paginator, 110 tasks_info=ComplexLogic.get_queues_size(), 111 users_builds=users_builds)
112
113 114 @coprs_ns.route("/fulltext/", defaults={"page": 1}) 115 @coprs_ns.route("/fulltext/<int:page>/") 116 -def coprs_fulltext_search(page=1):
117 fulltext = flask.request.args.get("fulltext", "") 118 try: 119 query = coprs_logic.CoprsLogic.get_multiple_fulltext(fulltext) 120 except ValueError as e: 121 flask.flash(str(e), "error") 122 return flask.redirect(flask.request.referrer or 123 flask.url_for("coprs_ns.coprs_show")) 124 125 paginator = helpers.Paginator(query, query.count(), page, 126 additional_params={"fulltext": fulltext}) 127 128 coprs = paginator.sliced_query 129 return render_template( 130 "coprs/show/fulltext.html", 131 coprs=coprs, 132 paginator=paginator, 133 fulltext=fulltext, 134 tasks_info=ComplexLogic.get_queues_size(), 135 )
136
137 138 @coprs_ns.route("/<username>/add/") 139 @login_required 140 -def copr_add(username):
141 form = forms.CoprFormFactory.create_form_cls()() 142 143 return flask.render_template("coprs/add.html", form=form)
144
145 146 @coprs_ns.route("/g/<group_name>/add/") 147 @login_required 148 -def group_copr_add(group_name):
149 group = ComplexLogic.get_group_by_name_safe(group_name) 150 form = forms.CoprFormFactory.create_form_cls()() 151 152 return flask.render_template( 153 "coprs/group_add.html", form=form, group=group)
154 155 156 @coprs_ns.route("/g/<group_name>/new/", methods=["POST"])
157 @login_required 158 -def group_copr_new(group_name):
159 group = ComplexLogic.get_group_by_name_safe(group_name) 160 form = forms.CoprFormFactory.create_form_cls(group=group)() 161 162 if form.validate_on_submit(): 163 try: 164 copr = coprs_logic.CoprsLogic.add( 165 flask.g.user, 166 name=form.name.data, 167 homepage=form.homepage.data, 168 contact=form.contact.data, 169 repos=form.repos.data.replace("\n", " "), 170 selected_chroots=form.selected_chroots, 171 description=form.description.data, 172 instructions=form.instructions.data, 173 disable_createrepo=form.disable_createrepo.data, 174 build_enable_net=form.build_enable_net.data, 175 unlisted_on_hp=form.unlisted_on_hp.data, 176 group=group, 177 persistent=form.persistent.data, 178 auto_prune=(form.auto_prune.data if flask.g.user.admin else True), 179 ) 180 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e: 181 flask.flash(str(e), "error") 182 return flask.render_template("coprs/group_add.html", form=form, group=group) 183 184 db.session.add(copr) 185 db.session.commit() 186 after_the_project_creation(copr, form) 187 188 return flask.redirect(url_for_copr_details(copr)) 189 else: 190 return flask.render_template("coprs/group_add.html", form=form, group=group)
191 192 193 @coprs_ns.route("/<username>/new/", methods=["POST"])
194 @login_required 195 -def copr_new(username):
196 """ 197 Receive information from the user on how to create its new copr 198 and create it accordingly. 199 """ 200 201 form = forms.CoprFormFactory.create_form_cls()() 202 if form.validate_on_submit(): 203 try: 204 copr = coprs_logic.CoprsLogic.add( 205 flask.g.user, 206 name=form.name.data, 207 homepage=form.homepage.data, 208 contact=form.contact.data, 209 repos=form.repos.data.replace("\n", " "), 210 selected_chroots=form.selected_chroots, 211 description=form.description.data, 212 instructions=form.instructions.data, 213 disable_createrepo=form.disable_createrepo.data, 214 build_enable_net=form.build_enable_net.data, 215 unlisted_on_hp=form.unlisted_on_hp.data, 216 persistent=form.persistent.data, 217 auto_prune=(form.auto_prune.data if flask.g.user.admin else True), 218 ) 219 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e: 220 flask.flash(str(e), "error") 221 return flask.render_template("coprs/add.html", form=form) 222 223 db.session.commit() 224 after_the_project_creation(copr, form) 225 226 return flask.redirect(url_for_copr_details(copr)) 227 else: 228 return flask.render_template("coprs/add.html", form=form)
229
230 231 -def after_the_project_creation(copr, form):
232 flask.flash("New project has been created successfully.", "success") 233 _check_rpmfusion(copr.repos) 234 if form.initial_pkgs.data: 235 pkgs = form.initial_pkgs.data.replace("\n", " ").split(" ") 236 237 # validate (and skip bad) urls 238 bad_urls = [] 239 for pkg in pkgs: 240 if not re.match("^.*\.src\.rpm$", pkg): 241 bad_urls.append(pkg) 242 flask.flash("Bad url: {0} (skipped)".format(pkg)) 243 for bad_url in bad_urls: 244 pkgs.remove(bad_url) 245 246 if not pkgs: 247 flask.flash("No initial packages submitted") 248 else: 249 # build each package as a separate build 250 for pkg in pkgs: 251 builds_logic.BuildsLogic.add( 252 flask.g.user, 253 pkgs=pkg, 254 copr=copr, 255 enable_net=form.build_enable_net.data 256 ) 257 258 db.session.commit() 259 flask.flash("Initial packages were successfully submitted " 260 "for building.")
261
262 263 @coprs_ns.route("/<username>/<coprname>/report-abuse") 264 @req_with_copr 265 @login_required 266 -def copr_report_abuse(copr):
267 return render_copr_report_abuse(copr)
268
269 270 @coprs_ns.route("/g/<group_name>/<coprname>/report-abuse") 271 @req_with_copr 272 @login_required 273 -def group_copr_report_abuse(copr):
274 return render_copr_report_abuse(copr)
275
276 277 -def render_copr_report_abuse(copr):
278 form = forms.CoprLegalFlagForm() 279 return render_template("coprs/report_abuse.html", copr=copr, form=form)
280
281 282 @coprs_ns.route("/g/<group_name>/<coprname>/") 283 @req_with_copr 284 -def group_copr_detail(copr):
285 return render_copr_detail(copr)
286
287 288 @coprs_ns.route("/<username>/<coprname>/") 289 @req_with_copr 290 -def copr_detail(copr):
291 if copr.is_a_group_project: 292 return flask.redirect(url_for_copr_details(copr)) 293 return render_copr_detail(copr)
294
295 296 -def render_copr_detail(copr):
297 repo_dl_stat = CounterStatLogic.get_copr_repo_dl_stat(copr) 298 form = forms.CoprLegalFlagForm() 299 repos_info = {} 300 for chroot in copr.active_chroots: 301 # chroot_rpms_dl_stat_key = CHROOT_REPO_MD_DL_STAT_FMT.format( 302 # copr_user=copr.user.name, 303 # copr_project_name=copr.name, 304 # copr_chroot=chroot.name, 305 # ) 306 chroot_rpms_dl_stat_key = CHROOT_RPMS_DL_STAT_FMT.format( 307 copr_user=copr.user.name, 308 copr_project_name=copr.name, 309 copr_chroot=chroot.name, 310 ) 311 chroot_rpms_dl_stat = TimedStatEvents.get_count( 312 rconnect=rcp.get_connection(), 313 name=chroot_rpms_dl_stat_key, 314 ) 315 316 logoset = set() 317 logodir = app.static_folder + "/chroot_logodir" 318 for logo in os.listdir(logodir): 319 # glob.glob() uses listdir() and fnmatch anyways 320 if fnmatch.fnmatch(logo, "*.png"): 321 logoset.add(logo.strip(".png")) 322 323 if chroot.name_release not in repos_info: 324 logo = None 325 if chroot.name_release in logoset: 326 logo = chroot.name_release + ".png" 327 elif chroot.os_release in logoset: 328 logo = chroot.os_release + ".png" 329 330 repos_info[chroot.name_release] = { 331 "name_release": chroot.name_release, 332 "name_release_human": chroot.name_release_human, 333 "os_release": chroot.os_release, 334 "os_version": chroot.os_version, 335 "logo": logo, 336 "arch_list": [chroot.arch], 337 "repo_file": "{}-{}.repo".format(copr.repo_id, chroot.name_release), 338 "dl_stat": repo_dl_stat[chroot.name_release], 339 "rpm_dl_stat": { 340 chroot.arch: chroot_rpms_dl_stat 341 } 342 } 343 else: 344 repos_info[chroot.name_release]["arch_list"].append(chroot.arch) 345 repos_info[chroot.name_release]["rpm_dl_stat"][chroot.arch] = chroot_rpms_dl_stat 346 repos_info_list = sorted(repos_info.values(), key=lambda rec: rec["name_release"]) 347 builds = builds_logic.BuildsLogic.get_multiple_by_copr(copr=copr).limit(1).all() 348 349 return flask.render_template( 350 "coprs/detail/overview.html", 351 copr=copr, 352 user=flask.g.user, 353 form=form, 354 repo_dl_stat=repo_dl_stat, 355 repos_info_list=repos_info_list, 356 latest_build=builds[0] if len(builds) == 1 else None, 357 )
358
359 360 @coprs_ns.route("/<username>/<coprname>/permissions/") 361 @req_with_copr 362 -def copr_permissions(copr):
363 permissions = coprs_logic.CoprPermissionsLogic.get_for_copr(copr).all() 364 if flask.g.user: 365 user_perm = flask.g.user.permissions_for_copr(copr) 366 else: 367 user_perm = None 368 369 permissions_applier_form = None 370 permissions_form = None 371 372 # generate a proper form for displaying 373 if flask.g.user: 374 # https://github.com/ajford/flask-wtf/issues/58 375 permissions_applier_form = \ 376 forms.PermissionsApplierFormFactory.create_form_cls( 377 user_perm)(formdata=None) 378 379 if flask.g.user.can_edit(copr): 380 permissions_form = forms.PermissionsFormFactory.create_form_cls( 381 permissions)() 382 383 return flask.render_template( 384 "coprs/detail/settings/permissions.html", 385 copr=copr, 386 permissions_form=permissions_form, 387 permissions_applier_form=permissions_applier_form, 388 permissions=permissions, 389 current_user_permissions=user_perm)
390
391 392 -def render_copr_webhooks(copr):
393 if not copr.webhook_secret: 394 copr.webhook_secret = uuid.uuid4() 395 db.session.add(copr) 396 db.session.commit() 397 398 github_url = "https://{}/webhooks/github/{}/{}/".format( 399 app.config["PUBLIC_COPR_HOSTNAME"], 400 copr.id, 401 copr.webhook_secret) 402 403 return flask.render_template( 404 "coprs/detail/settings/webhooks.html", 405 copr=copr, github_url=github_url)
406
407 408 @coprs_ns.route("/g/<group_name>/<coprname>/webhooks/") 409 @login_required 410 @req_with_copr 411 -def group_copr_webhooks(copr):
412 return render_copr_webhooks(copr)
413
414 415 @coprs_ns.route("/<username>/<coprname>/webhooks/") 416 @login_required 417 @req_with_copr 418 -def copr_webhooks(copr):
419 return render_copr_webhooks(copr)
420
421 422 -def render_copr_edit(copr, form, view):
423 if not form: 424 form = forms.CoprFormFactory.create_form_cls( 425 copr.mock_chroots)(obj=copr) 426 return flask.render_template( 427 "coprs/detail/settings/edit.html", 428 copr=copr, form=form, view=view)
429
430 431 @coprs_ns.route("/g/<group_name>/<coprname>/edit/") 432 @login_required 433 @req_with_copr 434 -def group_copr_edit(copr, form=None):
435 return render_copr_edit(copr, form, 'coprs_ns.copr_update')
436
437 438 @coprs_ns.route("/<username>/<coprname>/edit/") 439 @login_required 440 @req_with_copr 441 -def copr_edit(copr, form=None):
442 return render_copr_edit(copr, form, 'coprs_ns.copr_update')
443
444 445 -def _check_rpmfusion(repos):
446 if "rpmfusion" in repos: 447 message = flask.Markup('Using rpmfusion as dependency is nearly always wrong. Please see <a href="https://fedorahosted.org/copr/wiki/UserDocs#WhatIcanbuildinCopr">What I can build in Copr</a>.') 448 flask.flash(message, "error")
449
450 451 -def process_copr_update(copr, form):
452 copr.name = form.name.data 453 copr.homepage = form.homepage.data 454 copr.contact = form.contact.data 455 copr.repos = form.repos.data.replace("\n", " ") 456 copr.description = form.description.data 457 copr.instructions = form.instructions.data 458 copr.disable_createrepo = form.disable_createrepo.data 459 copr.build_enable_net = form.build_enable_net.data 460 copr.unlisted_on_hp = form.unlisted_on_hp.data 461 if flask.g.user.admin: 462 copr.auto_prune = form.auto_prune.data 463 else: 464 copr.auto_prune = True 465 coprs_logic.CoprChrootsLogic.update_from_names( 466 flask.g.user, copr, form.selected_chroots) 467 try: 468 # form validation checks for duplicates 469 coprs_logic.CoprsLogic.update(flask.g.user, copr) 470 except (exceptions.ActionInProgressException, 471 exceptions.InsufficientRightsException) as e: 472 473 flask.flash(str(e), "error") 474 db.session.rollback() 475 else: 476 flask.flash("Project has been updated successfully.", "success") 477 db.session.commit() 478 _check_rpmfusion(copr.repos)
479 480 481 @coprs_ns.route("/g/<group_name>/<coprname>/update/", methods=["POST"])
482 @login_required 483 @req_with_copr 484 -def group_copr_update(copr):
485 form = forms.CoprFormFactory.create_form_cls(group=copr.group)() 486 487 if form.validate_on_submit(): 488 process_copr_update(copr, form) 489 return flask.redirect(url_for( 490 "coprs_ns.group_copr_detail", 491 group_name=copr.group.name, coprname=copr.name 492 )) 493 494 else: 495 return group_copr_edit(group_name=copr.group.name, coprname=copr.name, form=form)
496 497 498 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
499 @login_required 500 @req_with_copr 501 -def copr_update(copr):
502 form = forms.CoprFormFactory.create_form_cls()() 503 504 if form.validate_on_submit(): 505 process_copr_update(copr, form) 506 return flask.redirect(url_for_copr_details(copr)) 507 else: 508 return render_copr_edit(copr, form, 'coprs_ns.copr_update')
509 510 511 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/", 512 methods=["POST"])
513 @login_required 514 @req_with_copr 515 -def copr_permissions_applier_change(copr):
516 permission = coprs_logic.CoprPermissionsLogic.get(copr, flask.g.user).first() 517 applier_permissions_form = \ 518 forms.PermissionsApplierFormFactory.create_form_cls(permission)() 519 520 if copr.user == flask.g.user: 521 flask.flash("Owner cannot request permissions for his own project.", "error") 522 elif applier_permissions_form.validate_on_submit(): 523 # we rely on these to be 0 or 1 from form. TODO: abstract from that 524 if permission is not None: 525 old_builder = permission.copr_builder 526 old_admin = permission.copr_admin 527 else: 528 old_builder = 0 529 old_admin = 0 530 new_builder = applier_permissions_form.copr_builder.data 531 new_admin = applier_permissions_form.copr_admin.data 532 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier( 533 flask.g.user, copr, permission, new_builder, new_admin) 534 db.session.commit() 535 flask.flash( 536 "Successfuly updated permissions for project '{0}'." 537 .format(copr.name)) 538 admin_mails = [copr.user.mail] 539 for perm in copr.copr_permissions: 540 # this 2 means that his status (admin) is approved 541 if perm.copr_admin == 2: 542 admin_mails.append(perm.user.mail) 543 544 # sending emails 545 if flask.current_app.config.get("SEND_EMAILS", False): 546 for mail in admin_mails: 547 msg = MIMEText( 548 "{6} is asking for these permissions:\n\n" 549 "Builder: {0} -> {1}\nAdmin: {2} -> {3}\n\n" 550 "Project: {4}\nOwner: {5}".format( 551 helpers.PermissionEnum(old_builder), 552 helpers.PermissionEnum(new_builder), 553 helpers.PermissionEnum(old_admin), 554 helpers.PermissionEnum(new_admin), 555 copr.name, copr.user.name, flask.g.user.name)) 556 557 msg["Subject"] = "[Copr] {0}: {1} is asking permissons".format(copr.name, flask.g.user.name) 558 msg["From"] = "root@{0}".format(platform.node()) 559 msg["To"] = mail 560 s = smtplib.SMTP("localhost") 561 s.sendmail("root@{0}".format(platform.node()), mail, msg.as_string()) 562 s.quit() 563 564 return flask.redirect(flask.url_for("coprs_ns.copr_detail", 565 username=copr.user.name, 566 coprname=copr.name))
567 568 569 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
570 @login_required 571 @req_with_copr 572 -def copr_update_permissions(copr):
573 permissions = copr.copr_permissions 574 permissions_form = forms.PermissionsFormFactory.create_form_cls( 575 permissions)() 576 577 if permissions_form.validate_on_submit(): 578 # we don't change owner (yet) 579 try: 580 # if admin is changing his permissions, his must be changed last 581 # so that we don't get InsufficientRightsException 582 permissions.sort( 583 key=lambda x: -1 if x.user_id == flask.g.user.id else 1) 584 for perm in permissions: 585 old_builder = perm.copr_builder 586 old_admin = perm.copr_admin 587 new_builder = permissions_form[ 588 "copr_builder_{0}".format(perm.user_id)].data 589 new_admin = permissions_form[ 590 "copr_admin_{0}".format(perm.user_id)].data 591 coprs_logic.CoprPermissionsLogic.update_permissions( 592 flask.g.user, copr, perm, new_builder, new_admin) 593 if flask.current_app.config.get("SEND_EMAILS", False) and \ 594 (old_builder is not new_builder or old_admin is not new_admin): 595 596 msg = MIMEText( 597 "Your permissions have changed:\n\n" 598 "Builder: {0} -> {1}\nAdmin: {2} -> {3}\n\n" 599 "Project: {4}\nOwner: {5}".format( 600 helpers.PermissionEnum(old_builder), 601 helpers.PermissionEnum(new_builder), 602 helpers.PermissionEnum(old_admin), 603 helpers.PermissionEnum(new_admin), 604 copr.name, copr.user.name)) 605 606 msg["Subject"] = "[Copr] {0}: Your permissions have changed".format(copr.name) 607 msg["From"] = "root@{0}".format(platform.node()) 608 msg["To"] = perm.user.mail 609 s = smtplib.SMTP("localhost") 610 s.sendmail("root@{0}".format(platform.node()), perm.user.mail, msg.as_string()) 611 s.quit() 612 # for now, we don't check for actions here, as permissions operation 613 # don't collide with any actions 614 except exceptions.InsufficientRightsException as e: 615 db.session.rollback() 616 flask.flash(str(e), "error") 617 else: 618 db.session.commit() 619 flask.flash("Project permissions were updated successfully.", "success") 620 621 return flask.redirect(url_for_copr_details(copr))
622 623 624 @coprs_ns.route("/id/<copr_id>/createrepo/", methods=["POST"])
625 @login_required 626 -def copr_createrepo(copr_id):
627 copr = ComplexLogic.get_copr_by_id_safe(copr_id) 628 629 chroots = [c.name for c in copr.active_chroots] 630 actions_logic.ActionsLogic.send_createrepo( 631 username=copr.owner_name, coprname=copr.name, 632 chroots=chroots) 633 634 db.session.commit() 635 flask.flash("Repository metadata will be regenerated in a few minutes ...") 636 return flask.redirect(url_for_copr_details(copr))
637
638 639 -def process_delete(copr, url_on_error, url_on_success):
640 form = forms.CoprDeleteForm() 641 if form.validate_on_submit(): 642 643 try: 644 ComplexLogic.delete_copr(copr) 645 except (exceptions.ActionInProgressException, 646 exceptions.InsufficientRightsException) as e: 647 648 db.session.rollback() 649 flask.flash(str(e), "error") 650 return flask.redirect(url_on_error) 651 else: 652 db.session.commit() 653 flask.flash("Project has been deleted successfully.") 654 return flask.redirect(url_on_success) 655 else: 656 return render_template("coprs/detail/settings/delete.html", form=form, copr=copr)
657 658 659 @coprs_ns.route("/<username>/<coprname>/delete/", methods=["GET", "POST"])
660 @login_required 661 @req_with_copr 662 -def copr_delete(copr):
663 return process_delete( 664 copr, 665 url_on_error=url_for("coprs_ns.copr_detail", 666 username=copr.user.name, coprname=copr.name), 667 url_on_success=url_for("coprs_ns.coprs_by_user", username=copr.user.username) 668 )
669 670 671 @coprs_ns.route("/g/<group_name>/<coprname>/delete/", methods=["GET", "POST"])
672 @login_required 673 @req_with_copr 674 -def group_copr_delete(copr):
675 676 return process_delete( 677 copr, 678 url_on_error=url_for('coprs_ns.group_copr_detail', 679 group_name=copr.group.name, coprname=copr.name), 680 url_on_success=url_for('groups_ns.list_projects_by_group', 681 group_name=copr.group.name) 682 )
683 684 685 @coprs_ns.route("/<username>/<coprname>/legal_flag/", methods=["POST"]) 691 692 693 @coprs_ns.route("/g/<group_name>/<coprname>/legal_flag/", methods=["POST"]) 699 731
732 733 @coprs_ns.route("/<username>/<coprname>/repo/<name_release>/", defaults={"repofile": None}) 734 @coprs_ns.route("/<username>/<coprname>/repo/<name_release>/<repofile>") 735 -def generate_repo_file(username, coprname, name_release, repofile):
736 """ Generate repo file for a given repo name. 737 Reponame = username-coprname """ 738 # This solution is used because flask splits off the last part after a 739 # dash, therefore user-re-po resolves to user-re/po instead of user/re-po 740 # FAS usernames may not contain dashes, so this construction is safe. 741 742 # support access to the group projects using @-notation 743 # todo: remove when yum/dnf plugin is updated to use new url schema 744 if username.startswith("@"): 745 return group_generate_repo_file(group_name=username[1:], coprname=coprname, 746 name_release=name_release, repofile=repofile) 747 748 copr = ComplexLogic.get_copr_safe(username, coprname) 749 return render_generate_repo_file(copr, name_release)
750
751 752 @coprs_ns.route("/g/<group_name>/<coprname>/repo/<name_release>/", defaults={"repofile": None}) 753 @coprs_ns.route("/g/<group_name>/<coprname>/repo/<name_release>/<repofile>") 754 @req_with_copr 755 -def group_generate_repo_file(copr, name_release, repofile):
756 """ Generate repo file for a given repo name. 757 Reponame = username-coprname """ 758 # This solution is used because flask splits off the last part after a 759 # dash, therefore user-re-po resolves to user-re/po instead of user/re-po 760 # FAS usernames may not contain dashes, so this construction is safe. 761 762 return render_generate_repo_file(copr, name_release)
763
764 765 -def render_generate_repo_file(copr, name_release):
766 767 # we need to check if we really got name release or it's a full chroot (caused by old dnf plugin) 768 if name_release in [c.name for c in copr.mock_chroots]: 769 chroot = [c for c in copr.mock_chroots if c.name == name_release][0] 770 kwargs = dict(coprname=copr.name, name_release=chroot.name_release) 771 if copr.is_a_group_project: 772 fixed_url = url_for("coprs_ns.group_generate_repo_file", 773 group_name=copr.group.name, **kwargs) 774 else: 775 fixed_url = url_for("coprs_ns.generate_repo_file", 776 username=copr.user.username, **kwargs) 777 return flask.redirect(fixed_url) 778 779 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first() 780 if not mock_chroot: 781 raise ObjectNotFound("Chroot {} does not exist".format(name_release)) 782 783 url = os.path.join(copr.repo_url, '') # adds trailing slash 784 repo_url = generate_repo_url(mock_chroot, url) 785 pubkey_url = urljoin(url, "pubkey.gpg") 786 response = flask.make_response( 787 flask.render_template("coprs/copr.repo", copr=copr, url=repo_url, pubkey_url=pubkey_url)) 788 response.mimetype = "text/plain" 789 response.headers["Content-Disposition"] = \ 790 "filename={0}.repo".format(copr.repo_name) 791 return response
792
793 794 ######################################################### 795 ### Module repo files ### 796 ######################################################### 797 798 @coprs_ns.route("/<username>/<coprname>/repo/modules/") 799 @coprs_ns.route("/@<group_name>/<coprname>/repo/modules/") 800 @coprs_ns.route("/g/<group_name>/<coprname>/repo/modules/") 801 @req_with_copr 802 -def generate_module_repo_file(copr):
803 """ Generate module repo file for a given project. """ 804 return render_generate_module_repo_file(copr)
805
806 -def render_generate_module_repo_file(copr):
807 url = os.path.join(copr.repo_url, '') # adds trailing slash 808 pubkey_url = urljoin(url, "pubkey.gpg") 809 response = flask.make_response( 810 flask.render_template("coprs/copr-modules.cfg", copr=copr, url=url, pubkey_url=pubkey_url)) 811 response.mimetype = "text/plain" 812 response.headers["Content-Disposition"] = \ 813 "filename={0}.cfg".format(copr.repo_name) 814 return response
815
816 ######################################################### 817 818 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>") 819 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
820 try: 821 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages") 822 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm: 823 response = flask.make_response(rpm.read()) 824 response.mimetype = "application/x-rpm" 825 response.headers["Content-Disposition"] = \ 826 "filename={0}".format(rpmfile) 827 return response 828 except IOError: 829 return flask.render_template("404.html")
830
831 832 -def render_monitor(copr, detailed=False):
833 monitor = builds_logic.BuildsMonitorLogic.get_monitor_data(copr) 834 oses = [chroot.os for chroot in copr.active_chroots_sorted] 835 oses_grouped = [(len(list(group)), key) for key, group in groupby(oses)] 836 archs = [chroot.arch for chroot in copr.active_chroots_sorted] 837 if detailed: 838 template = "coprs/detail/monitor/detailed.html" 839 else: 840 template = "coprs/detail/monitor/simple.html" 841 return flask.Response(stream_with_context(helpers.stream_template(template, 842 copr=copr, 843 monitor=monitor, 844 oses=oses_grouped, 845 archs=archs, 846 status_enum_func=helpers.StatusEnum)))
847
848 849 @coprs_ns.route("/<username>/<coprname>/monitor/") 850 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>") 851 @req_with_copr 852 -def copr_build_monitor(copr, detailed=False):
853 return render_monitor(copr, detailed == "detailed")
854
855 856 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/") 857 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>") 858 @req_with_copr 859 -def group_copr_build_monitor(copr, detailed=False):
860 return render_monitor(copr, detailed == "detailed")
861
862 863 @coprs_ns.route("/<username>/<coprname>/fork/") 864 @coprs_ns.route("/g/<group_name>/<coprname>/fork/") 865 @login_required 866 @req_with_copr 867 -def copr_fork(copr):
868 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)() 869 return render_copr_fork(copr, form)
870
871 872 -def render_copr_fork(copr, form, confirm=False):
873 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
874 875 876 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"]) 877 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
878 @login_required 879 @req_with_copr 880 -def copr_fork_post(copr):
881 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)() 882 if form.validate_on_submit(): 883 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0] 884 if flask.g.user.name != form.owner.data and not dstgroup: 885 return generic_error("There is no such group: {}".format(form.owner.data)) 886 887 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup) 888 if created: 889 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes " 890 "to duplicate a backend data.".format(copr.full_name, fcopr.full_name)) 891 elif not created and form.confirm.data == True: 892 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes " 893 "to duplicate a backend data.".format(copr.full_name, fcopr.full_name)) 894 else: 895 return render_copr_fork(copr, form, confirm=True) 896 897 db.session.commit() 898 flask.flash(msg) 899 900 return flask.redirect(url_for_copr_details(fcopr)) 901 return render_copr_fork(copr, form)
902 903 904 @coprs_ns.route("/update_search_index/", methods=["POST"])
905 -def copr_update_search_index():
906 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update_indexes_quick', '1']) 907 return "OK"
908
909 910 @coprs_ns.route("/<username>/<coprname>/modules/") 911 @coprs_ns.route("/g/<group_name>/<coprname>/modules/") 912 @req_with_copr 913 -def copr_modules(copr):
914 return render_copr_modules(copr)
915
916 917 -def render_copr_modules(copr):
918 modules = ModulesLogic.get_multiple_by_copr(copr=copr).all() 919 return flask.render_template("coprs/detail/modules.html", copr=copr, modules=modules)
920
921 922 @coprs_ns.route("/<username>/<coprname>/create_module/") 923 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/") 924 @login_required 925 @req_with_copr 926 -def copr_create_module(copr):
927 form = forms.CreateModuleForm() 928 return render_create_module(copr, form)
929
930 931 -def render_create_module(copr, form, profiles=2):
932 packages = [] 933 for build in filter(None, [p.last_build(successful=True) for p in copr.packages]): 934 for package in build.built_packages.split("\n"): 935 packages.append(package.split()[0]) 936 937 return flask.render_template("coprs/create_module.html", copr=copr, form=form, packages=packages, profiles=profiles)
938 939 940 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"]) 941 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
942 @login_required 943 @req_with_copr 944 -def copr_create_module_post(copr):
945 form = forms.CreateModuleForm(copr=copr, csrf_enabled=False) 946 args = [copr, form] 947 if "add_profile" in flask.request.values: 948 return add_profile(*args) 949 if "build_module" in flask.request.values: 950 return build_module(*args)
951 # @TODO Error
952 953 954 -def add_profile(copr, form):
955 n = len(form.profile_names) + 1 956 form.profile_names.append_entry() 957 for i in range(2, n): 958 form.profile_pkgs.append_entry() 959 return render_create_module(copr, form, profiles=n)
960
961 962 -def build_module(copr, form):
963 if not form.validate_on_submit(): 964 # WORKAROUND append those which are not in min_entries 965 for i in range(2, len(form.profile_names)): 966 form.profile_pkgs.append_entry() 967 return render_create_module(copr, form, profiles=len(form.profile_names)) 968 969 mmd = modulemd.ModuleMetadata() 970 mmd.name = str(copr.name) 971 mmd.stream = str(form.stream.data) 972 mmd.version = form.version.data 973 mmd.summary = "Module from Copr repository: {}".format(copr.full_name) 974 975 for package in form.filter.data: 976 mmd.filter.add_rpm(str(package)) 977 978 for package in form.api.data: 979 mmd.api.add_rpm(str(package)) 980 981 for i, values in enumerate(zip(form.profile_names.data, form.profile_pkgs.data)): 982 name, packages = values 983 mmd.profiles[name] = modulemd.profile.ModuleProfile() 984 for package in packages: 985 mmd.profiles[name].add_rpm(str(package)) 986 987 module = ModulesLogic.add(flask.g.user, copr, ModulesLogic.from_modulemd(mmd.dumps())) 988 db.session.flush() 989 actions_logic.ActionsLogic.send_build_module(flask.g.user, copr, module) 990 db.session.commit() 991 flask.flash("Modulemd yaml file successfully generated and submitted to be build", "success") 992 return flask.redirect(url_for_copr_details(copr))
993
994 995 @coprs_ns.route("/<username>/<coprname>/module/<id>") 996 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>") 997 @req_with_copr 998 -def copr_module(copr, id):
999 module = ModulesLogic.get(id).first() 1000 formatter = HtmlFormatter(style="autumn", linenos=False, noclasses=True) 1001 pretty_yaml = highlight(module.yaml, get_lexer_by_name("YAML"), formatter) 1002 return flask.render_template("coprs/detail/module.html", copr=copr, module=module, yaml=pretty_yaml)
1003
1004 1005 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw") 1006 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw") 1007 @req_with_copr 1008 -def copr_module_raw(copr, id):
1009 module = ModulesLogic.get(id).first() 1010 response = flask.make_response(module.yaml) 1011 response.mimetype = "text/plain" 1012 response.headers["Content-Disposition"] = \ 1013 "filename={}.yaml".format("-".join([str(module.id), module.name, module.stream, str(module.version)])) 1014 return response
1015