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