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