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