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, REPO_DL_STAT_FMT, CounterStatType 
49 50 -def url_for_copr_details(copr):
51 return url_for_copr_view( 52 "coprs_ns.copr_detail", 53 "coprs_ns.group_copr_detail", 54 copr)
55
56 57 -def url_for_copr_edit(copr):
58 return url_for_copr_view( 59 "coprs_ns.copr_edit", 60 "coprs_ns.group_copr_edit", 61 copr)
62
63 64 @coprs_ns.route("/", defaults={"page": 1}) 65 @coprs_ns.route("/<int:page>/") 66 -def coprs_show(page=1):
67 query = CoprsLogic.get_multiple(include_unlisted_on_hp=False) 68 query = CoprsLogic.set_query_order(query, desc=True) 69 70 paginator = helpers.Paginator(query, query.count(), page) 71 72 coprs = paginator.sliced_query 73 74 # flask.g.user is none when no user is logged - showing builds from everyone 75 # TODO: builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 5) takes too much time, optimize sql 76 # users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 5) 77 users_builds = builds_logic.BuildsLogic.get_recent_tasks(None, 5) 78 79 return flask.render_template("coprs/show/all.html", 80 coprs=coprs, 81 paginator=paginator, 82 tasks_info=ComplexLogic.get_queues_size(), 83 users_builds=users_builds)
84
85 86 @coprs_ns.route("/<username>/", defaults={"page": 1}) 87 @coprs_ns.route("/<username>/<int:page>/") 88 -def coprs_by_user(username=None, page=1):
89 user = users_logic.UsersLogic.get(username).first() 90 if not user: 91 return page_not_found( 92 "User {0} does not exist.".format(username)) 93 94 query = CoprsLogic.get_multiple_owned_by_username(username) 95 query = CoprsLogic.filter_without_group_projects(query) 96 query = CoprsLogic.set_query_order(query, desc=True) 97 98 paginator = helpers.Paginator(query, query.count(), page) 99 100 coprs = paginator.sliced_query 101 102 # flask.g.user is none when no user is logged - showing builds from everyone 103 users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 5) 104 105 return flask.render_template("coprs/show/user.html", 106 user=user, 107 coprs=coprs, 108 paginator=paginator, 109 tasks_info=ComplexLogic.get_queues_size(), 110 users_builds=users_builds)
111
112 113 @coprs_ns.route("/fulltext/", defaults={"page": 1}) 114 @coprs_ns.route("/fulltext/<int:page>/") 115 -def coprs_fulltext_search(page=1):
116 fulltext = flask.request.args.get("fulltext", "") 117 try: 118 query = coprs_logic.CoprsLogic.get_multiple_fulltext(fulltext) 119 except ValueError as e: 120 flask.flash(str(e), "error") 121 return flask.redirect(flask.request.referrer or 122 flask.url_for("coprs_ns.coprs_show")) 123 124 paginator = helpers.Paginator(query, query.count(), page, 125 additional_params={"fulltext": fulltext}) 126 127 coprs = paginator.sliced_query 128 return render_template( 129 "coprs/show/fulltext.html", 130 coprs=coprs, 131 paginator=paginator, 132 fulltext=fulltext, 133 tasks_info=ComplexLogic.get_queues_size(), 134 )
135
136 137 @coprs_ns.route("/<username>/add/") 138 @login_required 139 -def copr_add(username):
140 form = forms.CoprFormFactory.create_form_cls()() 141 142 return flask.render_template("coprs/add.html", form=form)
143
144 145 @coprs_ns.route("/g/<group_name>/add/") 146 @login_required 147 -def group_copr_add(group_name):
148 group = ComplexLogic.get_group_by_name_safe(group_name) 149 form = forms.CoprFormFactory.create_form_cls()() 150 151 return flask.render_template( 152 "coprs/group_add.html", form=form, group=group)
153 154 155 @coprs_ns.route("/g/<group_name>/new/", methods=["POST"])
156 @login_required 157 -def group_copr_new(group_name):
158 group = ComplexLogic.get_group_by_name_safe(group_name) 159 form = forms.CoprFormFactory.create_form_cls(group=group)() 160 161 if form.validate_on_submit(): 162 try: 163 copr = coprs_logic.CoprsLogic.add( 164 flask.g.user, 165 name=form.name.data, 166 homepage=form.homepage.data, 167 contact=form.contact.data, 168 repos=form.repos.data.replace("\n", " "), 169 selected_chroots=form.selected_chroots, 170 description=form.description.data, 171 instructions=form.instructions.data, 172 disable_createrepo=form.disable_createrepo.data, 173 build_enable_net=form.build_enable_net.data, 174 unlisted_on_hp=form.unlisted_on_hp.data, 175 group=group, 176 persistent=form.persistent.data, 177 auto_prune=(form.auto_prune.data if flask.g.user.admin else True), 178 use_bootstrap_container=form.use_bootstrap_container.data, 179 follow_fedora_branching=form.follow_fedora_branching.data, 180 ) 181 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e: 182 flask.flash(str(e), "error") 183 return flask.render_template("coprs/group_add.html", form=form, group=group) 184 185 db.session.add(copr) 186 db.session.commit() 187 after_the_project_creation(copr, form) 188 189 return flask.redirect(url_for_copr_details(copr)) 190 else: 191 return flask.render_template("coprs/group_add.html", form=form, group=group)
192 193 194 @coprs_ns.route("/<username>/new/", methods=["POST"])
195 @login_required 196 -def copr_new(username):
197 """ 198 Receive information from the user on how to create its new copr 199 and create it accordingly. 200 """ 201 202 form = forms.CoprFormFactory.create_form_cls()() 203 if form.validate_on_submit(): 204 try: 205 copr = coprs_logic.CoprsLogic.add( 206 flask.g.user, 207 name=form.name.data, 208 homepage=form.homepage.data, 209 contact=form.contact.data, 210 repos=form.repos.data.replace("\n", " "), 211 selected_chroots=form.selected_chroots, 212 description=form.description.data, 213 instructions=form.instructions.data, 214 disable_createrepo=form.disable_createrepo.data, 215 build_enable_net=form.build_enable_net.data, 216 unlisted_on_hp=form.unlisted_on_hp.data, 217 persistent=form.persistent.data, 218 auto_prune=(form.auto_prune.data if flask.g.user.admin else True), 219 use_bootstrap_container=form.use_bootstrap_container.data, 220 follow_fedora_branching=form.follow_fedora_branching.data, 221 ) 222 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e: 223 flask.flash(str(e), "error") 224 return flask.render_template("coprs/add.html", form=form) 225 226 db.session.commit() 227 after_the_project_creation(copr, form) 228 229 return flask.redirect(url_for_copr_details(copr)) 230 else: 231 return flask.render_template("coprs/add.html", form=form)
232
233 234 -def after_the_project_creation(copr, form):
235 flask.flash("New project has been created successfully.", "success") 236 _check_rpmfusion(copr.repos) 237 if form.initial_pkgs.data: 238 pkgs = form.initial_pkgs.data.replace("\n", " ").split(" ") 239 240 # validate (and skip bad) urls 241 bad_urls = [] 242 for pkg in pkgs: 243 if not pkg.endswith(".src.rpm"): 244 bad_urls.append(pkg) 245 flask.flash("Bad url: {0} (skipped)".format(pkg)) 246 for bad_url in bad_urls: 247 pkgs.remove(bad_url) 248 249 if not pkgs: 250 flask.flash("No initial packages submitted") 251 else: 252 # build each package as a separate build 253 for pkg in pkgs: 254 builds_logic.BuildsLogic.add( 255 flask.g.user, 256 pkgs=pkg, 257 srpm_url=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 807 name = REPO_DL_STAT_FMT.format(**{ 808 'copr_user': copr.user.name, 809 'copr_project_name': copr.name, 810 'copr_name_release': name_release, 811 }) 812 CounterStatLogic.incr(name=name, counter_type=CounterStatType.REPO_DL) 813 db.session.commit() 814 815 return response
816
817 818 ######################################################### 819 ### Module repo files ### 820 ######################################################### 821 822 @coprs_ns.route("/<username>/<coprname>/repo/modules/") 823 @coprs_ns.route("/@<group_name>/<coprname>/repo/modules/") 824 @coprs_ns.route("/g/<group_name>/<coprname>/repo/modules/") 825 @req_with_copr 826 -def generate_module_repo_file(copr):
827 """ Generate module repo file for a given project. """ 828 return render_generate_module_repo_file(copr)
829
830 -def render_generate_module_repo_file(copr):
831 url = os.path.join(copr.repo_url, '') # adds trailing slash 832 pubkey_url = urljoin(url, "pubkey.gpg") 833 response = flask.make_response( 834 flask.render_template("coprs/copr-modules.cfg", copr=copr, url=url, pubkey_url=pubkey_url)) 835 response.mimetype = "text/plain" 836 response.headers["Content-Disposition"] = \ 837 "filename={0}.cfg".format(copr.repo_name) 838 return response
839
840 ######################################################### 841 842 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>") 843 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
844 try: 845 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages") 846 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm: 847 response = flask.make_response(rpm.read()) 848 response.mimetype = "application/x-rpm" 849 response.headers["Content-Disposition"] = \ 850 "filename={0}".format(rpmfile) 851 return response 852 except IOError: 853 return flask.render_template("404.html")
854
855 856 -def render_monitor(copr, detailed=False):
857 monitor = builds_logic.BuildsMonitorLogic.get_monitor_data(copr) 858 oses = [chroot.os for chroot in copr.active_chroots_sorted] 859 oses_grouped = [(len(list(group)), key) for key, group in groupby(oses)] 860 archs = [chroot.arch for chroot in copr.active_chroots_sorted] 861 if detailed: 862 template = "coprs/detail/monitor/detailed.html" 863 else: 864 template = "coprs/detail/monitor/simple.html" 865 return flask.Response(stream_with_context(helpers.stream_template(template, 866 copr=copr, 867 monitor=monitor, 868 oses=oses_grouped, 869 archs=archs, 870 status_enum_func=helpers.StatusEnum)))
871
872 873 @coprs_ns.route("/<username>/<coprname>/monitor/") 874 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>") 875 @req_with_copr 876 -def copr_build_monitor(copr, detailed=False):
877 return render_monitor(copr, detailed == "detailed")
878
879 880 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/") 881 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>") 882 @req_with_copr 883 -def group_copr_build_monitor(copr, detailed=False):
884 return render_monitor(copr, detailed == "detailed")
885
886 887 @coprs_ns.route("/<username>/<coprname>/fork/") 888 @coprs_ns.route("/g/<group_name>/<coprname>/fork/") 889 @login_required 890 @req_with_copr 891 -def copr_fork(copr):
892 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)() 893 return render_copr_fork(copr, form)
894
895 896 -def render_copr_fork(copr, form, confirm=False):
897 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
898 899 900 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"]) 901 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
902 @login_required 903 @req_with_copr 904 -def copr_fork_post(copr):
905 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)() 906 if form.validate_on_submit(): 907 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0] 908 if flask.g.user.name != form.owner.data and not dstgroup: 909 return generic_error("There is no such group: {}".format(form.owner.data)) 910 911 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup) 912 if created: 913 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes " 914 "to duplicate a backend data.".format(copr.full_name, fcopr.full_name)) 915 elif not created and form.confirm.data == True: 916 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes " 917 "to duplicate a backend data.".format(copr.full_name, fcopr.full_name)) 918 else: 919 return render_copr_fork(copr, form, confirm=True) 920 921 db.session.commit() 922 flask.flash(msg) 923 924 return flask.redirect(url_for_copr_details(fcopr)) 925 return render_copr_fork(copr, form)
926 927 928 @coprs_ns.route("/update_search_index/", methods=["POST"])
929 -def copr_update_search_index():
930 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update_indexes_quick', '1']) 931 return "OK"
932
933 934 @coprs_ns.route("/<username>/<coprname>/modules/") 935 @coprs_ns.route("/g/<group_name>/<coprname>/modules/") 936 @req_with_copr 937 -def copr_modules(copr):
938 return render_copr_modules(copr)
939
940 941 -def render_copr_modules(copr):
942 modules = ModulesLogic.get_multiple_by_copr(copr=copr).all() 943 return flask.render_template("coprs/detail/modules.html", copr=copr, modules=modules)
944
945 946 @coprs_ns.route("/<username>/<coprname>/create_module/") 947 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/") 948 @login_required 949 @req_with_copr 950 -def copr_create_module(copr):
951 form = forms.CreateModuleForm() 952 return render_create_module(copr, form)
953
954 955 -def render_create_module(copr, form, profiles=2):
956 built_packages = [] 957 for build in filter(None, [p.last_build(successful=True) for p in copr.packages]): 958 for package in build.built_packages.split("\n"): 959 built_packages.append((package.split()[0], build)) 960 961 return flask.render_template("coprs/create_module.html", copr=copr, form=form, built_packages=built_packages, profiles=profiles)
962 963 964 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"]) 965 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
966 @login_required 967 @req_with_copr 968 -def copr_create_module_post(copr):
969 form = forms.CreateModuleForm(copr=copr, csrf_enabled=False) 970 args = [copr, form] 971 if "add_profile" in flask.request.values: 972 return add_profile(*args) 973 if "build_module" in flask.request.values: 974 return build_module(*args)
975 # @TODO Error
976 977 978 -def add_profile(copr, form):
979 n = len(form.profile_names) + 1 980 form.profile_names.append_entry() 981 for i in range(2, n): 982 form.profile_pkgs.append_entry() 983 return render_create_module(copr, form, profiles=n)
984
985 986 -def build_module(copr, form):
987 if not form.validate_on_submit(): 988 # WORKAROUND append those which are not in min_entries 989 for i in range(2, len(form.profile_names)): 990 form.profile_pkgs.append_entry() 991 return render_create_module(copr, form, profiles=len(form.profile_names)) 992 993 summary = "Module from Copr repository: {}".format(copr.full_name) 994 generator = ModulemdGenerator(str(copr.name), str(form.stream.data), 995 form.version.data, summary, app.config) 996 generator.add_filter(form.filter.data) 997 generator.add_api(form.api.data) 998 generator.add_profiles(enumerate(zip(form.profile_names.data, form.profile_pkgs.data))) 999 generator.add_components(form.packages.data, form.filter.data, form.builds.data) 1000 generator.add_base_runtime() 1001 tmp = tempfile.mktemp() 1002 generator.dump(tmp) 1003 1004 proxy = MBSProxy(mbs_url=flask.current_app.config["MBS_URL"], user_name=flask.g.user.name) 1005 with open(tmp) as tmp_handle: 1006 response = proxy.build_module(copr.owner_name, copr.name, generator.nsv, tmp_handle) 1007 os.remove(tmp) 1008 1009 if response.failed: 1010 flask.flash(response.message, "error") 1011 return render_create_module(copr, form, len(form.profile_names)) 1012 flask.flash("Modulemd yaml file successfully generated and submitted to be build", "success") 1013 return flask.redirect(url_for_copr_details(copr))
1014
1015 1016 @coprs_ns.route("/<username>/<coprname>/module/<id>") 1017 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>") 1018 @req_with_copr 1019 -def copr_module(copr, id):
1020 module = ModulesLogic.get(id).first() 1021 formatter = HtmlFormatter(style="autumn", linenos=False, noclasses=True) 1022 pretty_yaml = highlight(module.yaml, get_lexer_by_name("YAML"), formatter) 1023 return flask.render_template("coprs/detail/module.html", copr=copr, module=module, yaml=pretty_yaml)
1024
1025 1026 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw") 1027 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw") 1028 @req_with_copr 1029 -def copr_module_raw(copr, id):
1030 module = ModulesLogic.get(id).first() 1031 response = flask.make_response(module.yaml) 1032 response.mimetype = "text/plain" 1033 response.headers["Content-Disposition"] = \ 1034 "filename={}.yaml".format("-".join([str(module.id), module.name, module.stream, str(module.version)])) 1035 return response
1036