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