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