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

Source Code for Module coprs.views.coprs_ns.coprs_general

   1  # coding: utf-8 
   2   
   3  import os 
   4  import time 
   5  import fnmatch 
   6  import re 
   7  import uuid 
   8  import subprocess 
   9  from six.moves.urllib.parse import urljoin 
  10   
  11  import flask 
  12  from flask import render_template, url_for, stream_with_context 
  13  import platform 
  14  import smtplib 
  15  import tempfile 
  16  import sqlalchemy 
  17  import modulemd 
  18  from email.mime.text import MIMEText 
  19  from itertools import groupby 
  20   
  21  from pygments import highlight 
  22  from pygments.lexers import get_lexer_by_name 
  23  from pygments.formatters import HtmlFormatter 
  24   
  25  from coprs import app 
  26  from coprs import db 
  27  from coprs import rcp 
  28  from coprs import exceptions 
  29  from coprs import forms 
  30  from coprs import helpers 
  31  from coprs import models 
  32  from coprs.exceptions import ObjectNotFound 
  33  from coprs.logic.coprs_logic import CoprsLogic 
  34  from coprs.logic.packages_logic import PackagesLogic 
  35  from coprs.logic.stat_logic import CounterStatLogic 
  36  from coprs.logic.users_logic import UsersLogic 
  37  from coprs.logic.modules_logic import ModulesLogic, ModulemdGenerator, MBSProxy 
  38  from coprs.rmodels import TimedStatEvents 
  39   
  40  from coprs.logic.complex_logic import ComplexLogic 
  41   
  42  from coprs.views.misc import login_required, page_not_found, req_with_copr, req_with_copr, generic_error 
  43   
  44  from coprs.views.coprs_ns import coprs_ns 
  45  from coprs.views.groups_ns import groups_ns 
  46   
  47  from coprs.logic import builds_logic, coprs_logic, actions_logic, users_logic 
  48  from coprs.helpers import parse_package_name, generate_repo_url, CHROOT_RPMS_DL_STAT_FMT, CHROOT_REPO_MD_DL_STAT_FMT, \ 
  49      str2bool, url_for_copr_view 
50 51 52 -def url_for_copr_details(copr):
53 return url_for_copr_view( 54 "coprs_ns.copr_detail", 55 "coprs_ns.group_copr_detail", 56 copr)
57
58 59 -def url_for_copr_edit(copr):
60 return url_for_copr_view( 61 "coprs_ns.copr_edit", 62 "coprs_ns.group_copr_edit", 63 copr)
64
65 66 @coprs_ns.route("/", defaults={"page": 1}) 67 @coprs_ns.route("/<int:page>/") 68 -def coprs_show(page=1):
69 query = CoprsLogic.get_multiple(include_unlisted_on_hp=False) 70 query = CoprsLogic.set_query_order(query, desc=True) 71 72 paginator = helpers.Paginator(query, query.count(), page) 73 74 coprs = paginator.sliced_query 75 76 # flask.g.user is none when no user is logged - showing builds from everyone 77 # TODO: builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 5) takes too much time, optimize sql 78 # users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 5) 79 users_builds = builds_logic.BuildsLogic.get_recent_tasks(None, 5) 80 81 return flask.render_template("coprs/show/all.html", 82 coprs=coprs, 83 paginator=paginator, 84 tasks_info=ComplexLogic.get_queues_size(), 85 users_builds=users_builds)
86
87 88 @coprs_ns.route("/<username>/", defaults={"page": 1}) 89 @coprs_ns.route("/<username>/<int:page>/") 90 -def coprs_by_user(username=None, page=1):
91 user = users_logic.UsersLogic.get(username).first() 92 if not user: 93 return page_not_found( 94 "User {0} does not exist.".format(username)) 95 96 query = CoprsLogic.get_multiple_owned_by_username(username) 97 query = CoprsLogic.filter_without_group_projects(query) 98 query = CoprsLogic.set_query_order(query, desc=True) 99 100 paginator = helpers.Paginator(query, query.count(), page) 101 102 coprs = paginator.sliced_query 103 104 # flask.g.user is none when no user is logged - showing builds from everyone 105 users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 5) 106 107 return flask.render_template("coprs/show/user.html", 108 user=user, 109 coprs=coprs, 110 paginator=paginator, 111 tasks_info=ComplexLogic.get_queues_size(), 112 users_builds=users_builds)
113
114 115 @coprs_ns.route("/fulltext/", defaults={"page": 1}) 116 @coprs_ns.route("/fulltext/<int:page>/") 117 -def coprs_fulltext_search(page=1):
118 fulltext = flask.request.args.get("fulltext", "") 119 try: 120 query = coprs_logic.CoprsLogic.get_multiple_fulltext(fulltext) 121 except ValueError as e: 122 flask.flash(str(e), "error") 123 return flask.redirect(flask.request.referrer or 124 flask.url_for("coprs_ns.coprs_show")) 125 126 paginator = helpers.Paginator(query, query.count(), page, 127 additional_params={"fulltext": fulltext}) 128 129 coprs = paginator.sliced_query 130 return render_template( 131 "coprs/show/fulltext.html", 132 coprs=coprs, 133 paginator=paginator, 134 fulltext=fulltext, 135 tasks_info=ComplexLogic.get_queues_size(), 136 )
137
138 139 @coprs_ns.route("/<username>/add/") 140 @login_required 141 -def copr_add(username):
142 form = forms.CoprFormFactory.create_form_cls()() 143 144 return flask.render_template("coprs/add.html", form=form)
145
146 147 @coprs_ns.route("/g/<group_name>/add/") 148 @login_required 149 -def group_copr_add(group_name):
150 group = ComplexLogic.get_group_by_name_safe(group_name) 151 form = forms.CoprFormFactory.create_form_cls()() 152 153 return flask.render_template( 154 "coprs/group_add.html", form=form, group=group)
155 156 157 @coprs_ns.route("/g/<group_name>/new/", methods=["POST"])
158 @login_required 159 -def group_copr_new(group_name):
160 group = ComplexLogic.get_group_by_name_safe(group_name) 161 form = forms.CoprFormFactory.create_form_cls(group=group)() 162 163 if form.validate_on_submit(): 164 try: 165 copr = coprs_logic.CoprsLogic.add( 166 flask.g.user, 167 name=form.name.data, 168 homepage=form.homepage.data, 169 contact=form.contact.data, 170 repos=form.repos.data.replace("\n", " "), 171 selected_chroots=form.selected_chroots, 172 description=form.description.data, 173 instructions=form.instructions.data, 174 disable_createrepo=form.disable_createrepo.data, 175 build_enable_net=form.build_enable_net.data, 176 unlisted_on_hp=form.unlisted_on_hp.data, 177 group=group, 178 persistent=form.persistent.data, 179 auto_prune=(form.auto_prune.data if flask.g.user.admin else True), 180 use_bootstrap_container=form.use_bootstrap_container.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 ) 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 re.match("^.*\.src\.rpm$", pkg): 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 if flask.g.user.admin: 471 copr.auto_prune = form.auto_prune.data 472 else: 473 copr.auto_prune = True 474 coprs_logic.CoprChrootsLogic.update_from_names( 475 flask.g.user, copr, form.selected_chroots) 476 try: 477 # form validation checks for duplicates 478 coprs_logic.CoprsLogic.update(flask.g.user, copr) 479 except (exceptions.ActionInProgressException, 480 exceptions.InsufficientRightsException) as e: 481 482 flask.flash(str(e), "error") 483 db.session.rollback() 484 else: 485 flask.flash("Project has been updated successfully.", "success") 486 db.session.commit() 487 _check_rpmfusion(copr.repos)
488 489 490 @coprs_ns.route("/g/<group_name>/<coprname>/update/", methods=["POST"])
491 @login_required 492 @req_with_copr 493 -def group_copr_update(copr):
494 form = forms.CoprFormFactory.create_form_cls(group=copr.group)() 495 496 if form.validate_on_submit(): 497 process_copr_update(copr, form) 498 return flask.redirect(url_for( 499 "coprs_ns.group_copr_detail", 500 group_name=copr.group.name, coprname=copr.name 501 )) 502 503 else: 504 return group_copr_edit(group_name=copr.group.name, coprname=copr.name, form=form)
505 506 507 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
508 @login_required 509 @req_with_copr 510 -def copr_update(copr):
511 form = forms.CoprFormFactory.create_form_cls(user=copr.user)() 512 513 if form.validate_on_submit(): 514 process_copr_update(copr, form) 515 return flask.redirect(url_for_copr_details(copr)) 516 else: 517 return render_copr_edit(copr, form, 'coprs_ns.copr_update')
518 519 520 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/", 521 methods=["POST"])
522 @login_required 523 @req_with_copr 524 -def copr_permissions_applier_change(copr):
525 permission = coprs_logic.CoprPermissionsLogic.get(copr, flask.g.user).first() 526 applier_permissions_form = \ 527 forms.PermissionsApplierFormFactory.create_form_cls(permission)() 528 529 if copr.user == flask.g.user: 530 flask.flash("Owner cannot request permissions for his own project.", "error") 531 elif applier_permissions_form.validate_on_submit(): 532 # we rely on these to be 0 or 1 from form. TODO: abstract from that 533 if permission is not None: 534 old_builder = permission.copr_builder 535 old_admin = permission.copr_admin 536 else: 537 old_builder = 0 538 old_admin = 0 539 new_builder = applier_permissions_form.copr_builder.data 540 new_admin = applier_permissions_form.copr_admin.data 541 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier( 542 flask.g.user, copr, permission, new_builder, new_admin) 543 db.session.commit() 544 flask.flash( 545 "Successfuly updated permissions for project '{0}'." 546 .format(copr.name)) 547 admin_mails = [copr.user.mail] 548 for perm in copr.copr_permissions: 549 # this 2 means that his status (admin) is approved 550 if perm.copr_admin == 2: 551 admin_mails.append(perm.user.mail) 552 553 # sending emails 554 if flask.current_app.config.get("SEND_EMAILS", False): 555 for mail in admin_mails: 556 msg = MIMEText( 557 "{6} is asking for these permissions:\n\n" 558 "Builder: {0} -> {1}\nAdmin: {2} -> {3}\n\n" 559 "Project: {4}\nOwner: {5}".format( 560 helpers.PermissionEnum(old_builder), 561 helpers.PermissionEnum(new_builder), 562 helpers.PermissionEnum(old_admin), 563 helpers.PermissionEnum(new_admin), 564 copr.name, copr.user.name, flask.g.user.name)) 565 566 msg["Subject"] = "[Copr] {0}: {1} is asking permissons".format(copr.name, flask.g.user.name) 567 msg["From"] = "root@{0}".format(platform.node()) 568 msg["To"] = mail 569 s = smtplib.SMTP("localhost") 570 s.sendmail("root@{0}".format(platform.node()), mail, msg.as_string()) 571 s.quit() 572 573 return flask.redirect(flask.url_for("coprs_ns.copr_detail", 574 username=copr.user.name, 575 coprname=copr.name))
576 577 578 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
579 @login_required 580 @req_with_copr 581 -def copr_update_permissions(copr):
582 permissions = copr.copr_permissions 583 permissions_form = forms.PermissionsFormFactory.create_form_cls( 584 permissions)() 585 586 if permissions_form.validate_on_submit(): 587 # we don't change owner (yet) 588 try: 589 # if admin is changing his permissions, his must be changed last 590 # so that we don't get InsufficientRightsException 591 permissions.sort( 592 key=lambda x: -1 if x.user_id == flask.g.user.id else 1) 593 for perm in permissions: 594 old_builder = perm.copr_builder 595 old_admin = perm.copr_admin 596 new_builder = permissions_form[ 597 "copr_builder_{0}".format(perm.user_id)].data 598 new_admin = permissions_form[ 599 "copr_admin_{0}".format(perm.user_id)].data 600 coprs_logic.CoprPermissionsLogic.update_permissions( 601 flask.g.user, copr, perm, new_builder, new_admin) 602 if flask.current_app.config.get("SEND_EMAILS", False) and \ 603 (old_builder is not new_builder or old_admin is not new_admin): 604 605 msg = MIMEText( 606 "Your permissions have changed:\n\n" 607 "Builder: {0} -> {1}\nAdmin: {2} -> {3}\n\n" 608 "Project: {4}\nOwner: {5}".format( 609 helpers.PermissionEnum(old_builder), 610 helpers.PermissionEnum(new_builder), 611 helpers.PermissionEnum(old_admin), 612 helpers.PermissionEnum(new_admin), 613 copr.name, copr.user.name)) 614 615 msg["Subject"] = "[Copr] {0}: Your permissions have changed".format(copr.name) 616 msg["From"] = "root@{0}".format(platform.node()) 617 msg["To"] = perm.user.mail 618 s = smtplib.SMTP("localhost") 619 s.sendmail("root@{0}".format(platform.node()), perm.user.mail, msg.as_string()) 620 s.quit() 621 # for now, we don't check for actions here, as permissions operation 622 # don't collide with any actions 623 except exceptions.InsufficientRightsException as e: 624 db.session.rollback() 625 flask.flash(str(e), "error") 626 else: 627 db.session.commit() 628 flask.flash("Project permissions were updated successfully.", "success") 629 630 return flask.redirect(url_for_copr_details(copr))
631 632 633 @coprs_ns.route("/id/<copr_id>/createrepo/", methods=["POST"])
634 @login_required 635 -def copr_createrepo(copr_id):
636 copr = ComplexLogic.get_copr_by_id_safe(copr_id) 637 if not flask.g.user.can_edit(copr): 638 flask.flash( 639 "You are not allowed to recreate repository metadata of copr with id {}.".format(copr_id), "error") 640 return flask.redirect(url_for_copr_details(copr)) 641 642 chroots = [c.name for c in copr.active_chroots] 643 actions_logic.ActionsLogic.send_createrepo( 644 username=copr.owner_name, coprname=copr.name, 645 chroots=chroots) 646 647 db.session.commit() 648 flask.flash("Repository metadata will be regenerated in a few minutes ...") 649 return flask.redirect(url_for_copr_details(copr))
650
651 652 -def process_delete(copr, url_on_error, url_on_success):
653 form = forms.CoprDeleteForm() 654 if form.validate_on_submit(): 655 656 try: 657 ComplexLogic.delete_copr(copr) 658 except (exceptions.ActionInProgressException, 659 exceptions.InsufficientRightsException) as e: 660 661 db.session.rollback() 662 flask.flash(str(e), "error") 663 return flask.redirect(url_on_error) 664 else: 665 db.session.commit() 666 flask.flash("Project has been deleted successfully.") 667 return flask.redirect(url_on_success) 668 else: 669 return render_template("coprs/detail/settings/delete.html", form=form, copr=copr)
670 671 672 @coprs_ns.route("/<username>/<coprname>/delete/", methods=["GET", "POST"])
673 @login_required 674 @req_with_copr 675 -def copr_delete(copr):
676 return process_delete( 677 copr, 678 url_on_error=url_for("coprs_ns.copr_detail", 679 username=copr.user.name, coprname=copr.name), 680 url_on_success=url_for("coprs_ns.coprs_by_user", username=copr.user.username) 681 )
682 683 684 @coprs_ns.route("/g/<group_name>/<coprname>/delete/", methods=["GET", "POST"])
685 @login_required 686 @req_with_copr 687 -def group_copr_delete(copr):
688 689 return process_delete( 690 copr, 691 url_on_error=url_for('coprs_ns.group_copr_detail', 692 group_name=copr.group.name, coprname=copr.name), 693 url_on_success=url_for('groups_ns.list_projects_by_group', 694 group_name=copr.group.name) 695 )
696 697 698 @coprs_ns.route("/<username>/<coprname>/legal_flag/", methods=["POST"]) 704 705 706 @coprs_ns.route("/g/<group_name>/<coprname>/legal_flag/", methods=["POST"]) 712 744
745 746 @coprs_ns.route("/<username>/<coprname>/repo/<name_release>/", defaults={"repofile": None}) 747 @coprs_ns.route("/<username>/<coprname>/repo/<name_release>/<repofile>") 748 -def generate_repo_file(username, coprname, name_release, repofile):
749 """ Generate repo file for a given repo name. 750 Reponame = username-coprname """ 751 # This solution is used because flask splits off the last part after a 752 # dash, therefore user-re-po resolves to user-re/po instead of user/re-po 753 # FAS usernames may not contain dashes, so this construction is safe. 754 755 # support access to the group projects using @-notation 756 # todo: remove when yum/dnf plugin is updated to use new url schema 757 if username.startswith("@"): 758 return group_generate_repo_file(group_name=username[1:], coprname=coprname, 759 name_release=name_release, repofile=repofile) 760 761 copr = ComplexLogic.get_copr_safe(username, coprname) 762 return render_generate_repo_file(copr, name_release)
763
764 765 @coprs_ns.route("/g/<group_name>/<coprname>/repo/<name_release>/", defaults={"repofile": None}) 766 @coprs_ns.route("/g/<group_name>/<coprname>/repo/<name_release>/<repofile>") 767 @req_with_copr 768 -def group_generate_repo_file(copr, name_release, repofile):
769 """ Generate repo file for a given repo name. 770 Reponame = username-coprname """ 771 # This solution is used because flask splits off the last part after a 772 # dash, therefore user-re-po resolves to user-re/po instead of user/re-po 773 # FAS usernames may not contain dashes, so this construction is safe. 774 775 return render_generate_repo_file(copr, name_release)
776
777 778 -def render_generate_repo_file(copr, name_release):
779 780 # we need to check if we really got name release or it's a full chroot (caused by old dnf plugin) 781 if name_release in [c.name for c in copr.mock_chroots]: 782 chroot = [c for c in copr.mock_chroots if c.name == name_release][0] 783 kwargs = dict(coprname=copr.name, name_release=chroot.name_release) 784 if copr.is_a_group_project: 785 fixed_url = url_for("coprs_ns.group_generate_repo_file", 786 group_name=copr.group.name, **kwargs) 787 else: 788 fixed_url = url_for("coprs_ns.generate_repo_file", 789 username=copr.user.username, **kwargs) 790 return flask.redirect(fixed_url) 791 792 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first() 793 if not mock_chroot: 794 raise ObjectNotFound("Chroot {} does not exist".format(name_release)) 795 796 url = os.path.join(copr.repo_url, '') # adds trailing slash 797 repo_url = generate_repo_url(mock_chroot, url) 798 pubkey_url = urljoin(url, "pubkey.gpg") 799 response = flask.make_response( 800 flask.render_template("coprs/copr.repo", copr=copr, url=repo_url, pubkey_url=pubkey_url)) 801 response.mimetype = "text/plain" 802 response.headers["Content-Disposition"] = \ 803 "filename={0}.repo".format(copr.repo_name) 804 return response
805
806 807 ######################################################### 808 ### Module repo files ### 809 ######################################################### 810 811 @coprs_ns.route("/<username>/<coprname>/repo/modules/") 812 @coprs_ns.route("/@<group_name>/<coprname>/repo/modules/") 813 @coprs_ns.route("/g/<group_name>/<coprname>/repo/modules/") 814 @req_with_copr 815 -def generate_module_repo_file(copr):
816 """ Generate module repo file for a given project. """ 817 return render_generate_module_repo_file(copr)
818
819 -def render_generate_module_repo_file(copr):
820 url = os.path.join(copr.repo_url, '') # adds trailing slash 821 pubkey_url = urljoin(url, "pubkey.gpg") 822 response = flask.make_response( 823 flask.render_template("coprs/copr-modules.cfg", copr=copr, url=url, pubkey_url=pubkey_url)) 824 response.mimetype = "text/plain" 825 response.headers["Content-Disposition"] = \ 826 "filename={0}.cfg".format(copr.repo_name) 827 return response
828
829 ######################################################### 830 831 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>") 832 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
833 try: 834 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages") 835 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm: 836 response = flask.make_response(rpm.read()) 837 response.mimetype = "application/x-rpm" 838 response.headers["Content-Disposition"] = \ 839 "filename={0}".format(rpmfile) 840 return response 841 except IOError: 842 return flask.render_template("404.html")
843
844 845 -def render_monitor(copr, detailed=False):
846 monitor = builds_logic.BuildsMonitorLogic.get_monitor_data(copr) 847 oses = [chroot.os for chroot in copr.active_chroots_sorted] 848 oses_grouped = [(len(list(group)), key) for key, group in groupby(oses)] 849 archs = [chroot.arch for chroot in copr.active_chroots_sorted] 850 if detailed: 851 template = "coprs/detail/monitor/detailed.html" 852 else: 853 template = "coprs/detail/monitor/simple.html" 854 return flask.Response(stream_with_context(helpers.stream_template(template, 855 copr=copr, 856 monitor=monitor, 857 oses=oses_grouped, 858 archs=archs, 859 status_enum_func=helpers.StatusEnum)))
860
861 862 @coprs_ns.route("/<username>/<coprname>/monitor/") 863 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>") 864 @req_with_copr 865 -def copr_build_monitor(copr, detailed=False):
866 return render_monitor(copr, detailed == "detailed")
867
868 869 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/") 870 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>") 871 @req_with_copr 872 -def group_copr_build_monitor(copr, detailed=False):
873 return render_monitor(copr, detailed == "detailed")
874
875 876 @coprs_ns.route("/<username>/<coprname>/fork/") 877 @coprs_ns.route("/g/<group_name>/<coprname>/fork/") 878 @login_required 879 @req_with_copr 880 -def copr_fork(copr):
881 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)() 882 return render_copr_fork(copr, form)
883
884 885 -def render_copr_fork(copr, form, confirm=False):
886 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
887 888 889 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"]) 890 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
891 @login_required 892 @req_with_copr 893 -def copr_fork_post(copr):
894 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)() 895 if form.validate_on_submit(): 896 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0] 897 if flask.g.user.name != form.owner.data and not dstgroup: 898 return generic_error("There is no such group: {}".format(form.owner.data)) 899 900 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup) 901 if created: 902 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes " 903 "to duplicate a backend data.".format(copr.full_name, fcopr.full_name)) 904 elif not created and form.confirm.data == True: 905 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes " 906 "to duplicate a backend data.".format(copr.full_name, fcopr.full_name)) 907 else: 908 return render_copr_fork(copr, form, confirm=True) 909 910 db.session.commit() 911 flask.flash(msg) 912 913 return flask.redirect(url_for_copr_details(fcopr)) 914 return render_copr_fork(copr, form)
915 916 917 @coprs_ns.route("/update_search_index/", methods=["POST"])
918 -def copr_update_search_index():
919 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update_indexes_quick', '1']) 920 return "OK"
921
922 923 @coprs_ns.route("/<username>/<coprname>/modules/") 924 @coprs_ns.route("/g/<group_name>/<coprname>/modules/") 925 @req_with_copr 926 -def copr_modules(copr):
927 return render_copr_modules(copr)
928
929 930 -def render_copr_modules(copr):
931 modules = ModulesLogic.get_multiple_by_copr(copr=copr).all() 932 return flask.render_template("coprs/detail/modules.html", copr=copr, modules=modules)
933
934 935 @coprs_ns.route("/<username>/<coprname>/create_module/") 936 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/") 937 @login_required 938 @req_with_copr 939 -def copr_create_module(copr):
940 form = forms.CreateModuleForm() 941 return render_create_module(copr, form)
942
943 944 -def render_create_module(copr, form, profiles=2):
945 built_packages = [] 946 for build in filter(None, [p.last_build(successful=True) for p in copr.packages]): 947 for package in build.built_packages.split("\n"): 948 built_packages.append((package.split()[0], build)) 949 950 return flask.render_template("coprs/create_module.html", copr=copr, form=form, built_packages=built_packages, profiles=profiles)
951 952 953 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"]) 954 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
955 @login_required 956 @req_with_copr 957 -def copr_create_module_post(copr):
958 form = forms.CreateModuleForm(copr=copr, csrf_enabled=False) 959 args = [copr, form] 960 if "add_profile" in flask.request.values: 961 return add_profile(*args) 962 if "build_module" in flask.request.values: 963 return build_module(*args)
964 # @TODO Error
965 966 967 -def add_profile(copr, form):
968 n = len(form.profile_names) + 1 969 form.profile_names.append_entry() 970 for i in range(2, n): 971 form.profile_pkgs.append_entry() 972 return render_create_module(copr, form, profiles=n)
973
974 975 -def build_module(copr, form):
976 if not form.validate_on_submit(): 977 # WORKAROUND append those which are not in min_entries 978 for i in range(2, len(form.profile_names)): 979 form.profile_pkgs.append_entry() 980 return render_create_module(copr, form, profiles=len(form.profile_names)) 981 982 summary = "Module from Copr repository: {}".format(copr.full_name) 983 generator = ModulemdGenerator(str(copr.name), str(form.stream.data), 984 form.version.data, summary, app.config) 985 generator.add_filter(form.filter.data) 986 generator.add_api(form.api.data) 987 generator.add_profiles(enumerate(zip(form.profile_names.data, form.profile_pkgs.data))) 988 generator.add_components(form.packages.data, form.filter.data, form.builds.data) 989 generator.add_base_runtime() 990 tmp = tempfile.mktemp() 991 generator.dump(tmp) 992 993 proxy = MBSProxy(mbs_url=flask.current_app.config["MBS_URL"], user_name=flask.g.user.name) 994 with open(tmp) as tmp_handle: 995 response = proxy.build_module(copr.owner_name, copr.name, generator.nsv, tmp_handle) 996 os.remove(tmp) 997 998 if response.failed: 999 flask.flash(response.message, "error") 1000 return render_create_module(copr, form, len(form.profile_names)) 1001 flask.flash("Modulemd yaml file successfully generated and submitted to be build", "success") 1002 return flask.redirect(url_for_copr_details(copr))
1003
1004 1005 @coprs_ns.route("/<username>/<coprname>/module/<id>") 1006 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>") 1007 @req_with_copr 1008 -def copr_module(copr, id):
1009 module = ModulesLogic.get(id).first() 1010 formatter = HtmlFormatter(style="autumn", linenos=False, noclasses=True) 1011 pretty_yaml = highlight(module.yaml, get_lexer_by_name("YAML"), formatter) 1012 return flask.render_template("coprs/detail/module.html", copr=copr, module=module, yaml=pretty_yaml)
1013
1014 1015 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw") 1016 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw") 1017 @req_with_copr 1018 -def copr_module_raw(copr, id):
1019 module = ModulesLogic.get(id).first() 1020 response = flask.make_response(module.yaml) 1021 response.mimetype = "text/plain" 1022 response.headers["Content-Disposition"] = \ 1023 "filename={}.yaml".format("-".join([str(module.id), module.name, module.stream, str(module.version)])) 1024 return response
1025