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