1
2
3 import os
4 import time
5 import fnmatch
6 import subprocess
7 import json
8 import datetime
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 sqlalchemy
15 from itertools import groupby
16 from wtforms import ValidationError
17
18 from pygments import highlight
19 from pygments.lexers import get_lexer_by_name
20 from pygments.formatters import HtmlFormatter
21
22 from coprs import app
23 from coprs import cache
24 from coprs import db
25 from coprs import rcp
26 from coprs import exceptions
27 from coprs import forms
28 from coprs import helpers
29 from coprs import models
30 from coprs.exceptions import ObjectNotFound
31 from coprs.logic.coprs_logic import CoprsLogic, PinnedCoprsLogic, MockChrootsLogic
32 from coprs.logic.stat_logic import CounterStatLogic
33 from coprs.logic.modules_logic import ModulesLogic, ModulemdGenerator, ModuleBuildFacade
34 from coprs.rmodels import TimedStatEvents
35 from coprs.mail import send_mail, LegalFlagMessage, PermissionRequestMessage, PermissionChangeMessage
36
37 from coprs.logic.complex_logic import ComplexLogic
38 from coprs.logic.outdated_chroots_logic import OutdatedChrootsLogic
39
40 from coprs.views.misc import (login_required, page_not_found, req_with_copr,
41 generic_error, req_with_copr_dir)
42
43 from coprs.views.coprs_ns import coprs_ns
44
45 from coprs.logic import builds_logic, coprs_logic, actions_logic, users_logic
46 from coprs.helpers import generate_repo_url, CHROOT_RPMS_DL_STAT_FMT, \
47 url_for_copr_view, REPO_DL_STAT_FMT, CounterStatType, generate_repo_name, \
48 WorkList
52 return url_for_copr_view(
53 "coprs_ns.copr_detail",
54 "coprs_ns.copr_detail",
55 copr)
56
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
76
77
78 users_builds = builds_logic.BuildsLogic.get_recent_tasks(None, 4)
79
80 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
81
82 return flask.render_template("coprs/show/all.html",
83 coprs=coprs,
84 pinned=[],
85 paginator=paginator,
86 tasks_info=ComplexLogic.get_queue_sizes(),
87 users_builds=users_builds,
88 graph=data)
89
90
91 @coprs_ns.route("/<username>/", defaults={"page": 1})
92 @coprs_ns.route("/<username>/<int:page>/")
93 -def coprs_by_user(username=None, page=1):
94 user = users_logic.UsersLogic.get(username).first()
95 if not user:
96 return page_not_found(
97 "User {0} does not exist.".format(username))
98
99 pinned = [pin.copr for pin in PinnedCoprsLogic.get_by_user_id(user.id)] if page == 1 else []
100 query = CoprsLogic.get_multiple_owned_by_username(username)
101 query = CoprsLogic.filter_without_ids(query, [copr.id for copr in pinned])
102 query = CoprsLogic.filter_without_group_projects(query)
103 query = CoprsLogic.set_query_order(query, desc=True)
104
105 paginator = helpers.Paginator(query, query.count(), page)
106 coprs = paginator.sliced_query
107
108
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 pinned=pinned,
117 paginator=paginator,
118 tasks_info=ComplexLogic.get_queue_sizes(),
119 users_builds=users_builds,
120 graph=data)
121
122
123 @coprs_ns.route("/fulltext/", defaults={"page": 1})
124 @coprs_ns.route("/fulltext/<int:page>/")
125 -def coprs_fulltext_search(page=1):
126 fulltext = flask.request.args.get("fulltext", "")
127 try:
128 query = coprs_logic.CoprsLogic.get_multiple_fulltext(fulltext)
129 except ValueError as e:
130 flask.flash(str(e), "error")
131 return flask.redirect(flask.request.referrer or
132 flask.url_for("coprs_ns.coprs_show"))
133
134 paginator = helpers.Paginator(query, query.count(), page,
135 additional_params={"fulltext": fulltext})
136
137 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
138
139 coprs = paginator.sliced_query
140 return render_template("coprs/show/fulltext.html",
141 coprs=coprs,
142 pinned=[],
143 paginator=paginator,
144 fulltext=fulltext,
145 tasks_info=ComplexLogic.get_queue_sizes(),
146 graph=data)
147
148
149 @coprs_ns.route("/<username>/add/")
150 @coprs_ns.route("/g/<group_name>/add/")
151 @login_required
152 -def copr_add(username=None, group_name=None):
161
162
163 @coprs_ns.route("/<username>/new/", methods=["POST"])
164 @coprs_ns.route("/g/<group_name>/new/", methods=["POST"])
165 @login_required
166 -def copr_new(username=None, group_name=None):
167 """
168 Receive information from the user (and group) on how to create its new copr
169 and create it accordingly.
170 """
171 group = None
172 redirect = "coprs/add.html"
173 if group_name:
174 group = ComplexLogic.get_group_by_name_safe(group_name)
175 redirect = "coprs/group_add.html"
176
177 form = forms.CoprFormFactory.create_form_cls(group=group)()
178 if form.validate_on_submit():
179 try:
180 copr = coprs_logic.CoprsLogic.add(
181 flask.g.user,
182 name=form.name.data,
183 homepage=form.homepage.data,
184 contact=form.contact.data,
185 repos=form.repos.data.replace("\n", " "),
186 selected_chroots=form.selected_chroots,
187 description=form.description.data,
188 instructions=form.instructions.data,
189 disable_createrepo=form.disable_createrepo.data,
190 build_enable_net=form.build_enable_net.data,
191 unlisted_on_hp=form.unlisted_on_hp.data,
192 group=group,
193 persistent=form.persistent.data,
194 auto_prune=(form.auto_prune.data if flask.g.user.admin else True),
195 follow_fedora_branching=form.follow_fedora_branching.data,
196 delete_after_days=form.delete_after_days.data,
197 multilib=form.multilib.data,
198 runtime_dependencies=form.runtime_dependencies.data.replace("\n", " "),
199 bootstrap=form.bootstrap.data,
200 )
201
202 db.session.commit()
203 after_the_project_creation(copr, form)
204 return flask.redirect(url_for_copr_details(copr))
205 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e:
206 flask.flash(str(e), "error")
207
208 return flask.render_template(redirect, form=form, group=group)
209
212 flask.flash("New project has been created successfully.", "success")
213 _check_rpmfusion(copr.repos)
214 if form.initial_pkgs.data:
215 pkgs = form.initial_pkgs.data.replace("\n", " ").split(" ")
216
217
218 bad_urls = []
219 for pkg in pkgs:
220 if not pkg.endswith(".src.rpm"):
221 bad_urls.append(pkg)
222 flask.flash("Bad url: {0} (skipped)".format(pkg))
223 for bad_url in bad_urls:
224 pkgs.remove(bad_url)
225
226 if not pkgs:
227 flask.flash("No initial packages submitted")
228 else:
229
230 for pkg in pkgs:
231 builds_logic.BuildsLogic.add(
232 flask.g.user,
233 pkgs=pkg,
234 srpm_url=pkg,
235 copr=copr,
236 enable_net=form.build_enable_net.data
237 )
238
239 db.session.commit()
240 flask.flash("Initial packages were successfully submitted "
241 "for building.")
242
243
244 @coprs_ns.route("/<username>/<coprname>/report-abuse")
245 @coprs_ns.route("/g/<group_name>/<coprname>/report-abuse")
246 @req_with_copr
247 @login_required
248 -def copr_report_abuse(copr):
250
255
256
257 @coprs_ns.route("/<username>/<coprname>/")
258 @coprs_ns.route("/g/<group_name>/<coprname>/")
259 @req_with_copr
260 -def copr_detail(copr):
262
265 repo_dl_stat = CounterStatLogic.get_copr_repo_dl_stat(copr)
266 form = forms.CoprLegalFlagForm()
267 repos_info = {}
268 for chroot in copr.active_chroots:
269 chroot_rpms_dl_stat_key = CHROOT_RPMS_DL_STAT_FMT.format(
270 copr_user=copr.owner_name,
271 copr_project_name=copr.name,
272 copr_chroot=chroot.name,
273 )
274 chroot_rpms_dl_stat = TimedStatEvents.get_count(
275 rconnect=rcp.get_connection(),
276 name=chroot_rpms_dl_stat_key,
277 )
278
279 logoset = set()
280 logodir = app.static_folder + "/chroot_logodir"
281 for logo in os.listdir(logodir):
282
283 if fnmatch.fnmatch(logo, "*.png"):
284 logoset.add(logo[:-4])
285
286 if chroot.name_release not in repos_info:
287 logo = None
288 if chroot.name_release in logoset:
289 logo = chroot.name_release + ".png"
290 elif chroot.os_release in logoset:
291 logo = chroot.os_release + ".png"
292
293 repos_info[chroot.name_release] = {
294 "name_release": chroot.name_release,
295 "os_release": chroot.os_release,
296 "os_version": chroot.os_version,
297 "logo": logo,
298 "arch_list": [chroot.arch],
299 "repo_file": "{}-{}.repo".format(copr.repo_id, chroot.name_release),
300 "dl_stat": repo_dl_stat[chroot.name_release],
301 "rpm_dl_stat": {
302 chroot.arch: chroot_rpms_dl_stat
303 }
304 }
305 else:
306 repos_info[chroot.name_release]["arch_list"].append(chroot.arch)
307 repos_info[chroot.name_release]["rpm_dl_stat"][chroot.arch] = chroot_rpms_dl_stat
308
309 if copr.multilib:
310 for name_release in repos_info:
311 arches = repos_info[name_release]['arch_list']
312 arch_repos = {}
313 for ch64, ch32 in models.MockChroot.multilib_pairs.items():
314 if set([ch64, ch32]).issubset(set(arches)):
315 arch_repos[ch64] = ch32
316
317 repos_info[name_release]['arch_repos'] = arch_repos
318
319
320 repos_info_list = sorted(repos_info.values(), key=lambda rec: rec["name_release"])
321 builds = builds_logic.BuildsLogic.get_multiple_by_copr(copr=copr).limit(1).all()
322
323 return flask.render_template(
324 "coprs/detail/overview.html",
325 copr=copr,
326 user=flask.g.user,
327 form=form,
328 repo_dl_stat=repo_dl_stat,
329 repos_info_list=repos_info_list,
330 latest_build=builds[0] if len(builds) == 1 else None,
331 )
332
333
334 @coprs_ns.route("/<username>/<coprname>/", methods=["POST"])
335 @coprs_ns.route("/g/<group_name>/<coprname>/", methods=["POST"])
336 @req_with_copr
337 @login_required
338 -def copr_detail_post(copr):
339 form = forms.VoteForCopr(meta={'csrf': False})
340 if not form.validate_on_submit():
341 flask.flash(form.errors, "error")
342 return render_copr_detail(copr)
343
344
345 coprs_logic.CoprScoreLogic.reset(copr)
346
347 if form.upvote.data:
348 coprs_logic.CoprScoreLogic.upvote(copr)
349 if form.downvote.data:
350 coprs_logic.CoprScoreLogic.downvote(copr)
351 db.session.commit()
352
353
354
355
356 if flask.request.referrer:
357 return flask.redirect(flask.request.referrer)
358
359
360
361 return flask.redirect(helpers.copr_url("coprs_ns.copr_detail", copr))
362
363
364 @coprs_ns.route("/<username>/<coprname>/permissions/")
365 @coprs_ns.route("/g/<group_name>/<coprname>/permissions/")
366 @req_with_copr
367 -def copr_permissions(copr):
395
398 if not copr.webhook_secret:
399 copr.new_webhook_secret()
400 db.session.add(copr)
401 db.session.commit()
402
403 bitbucket_url = "https://{}/webhooks/bitbucket/{}/{}/".format(
404 app.config["PUBLIC_COPR_HOSTNAME"],
405 copr.id,
406 copr.webhook_secret)
407
408 github_url = "https://{}/webhooks/github/{}/{}/".format(
409 app.config["PUBLIC_COPR_HOSTNAME"],
410 copr.id,
411 copr.webhook_secret)
412
413 gitlab_url = "https://{}/webhooks/gitlab/{}/{}/".format(
414 app.config["PUBLIC_COPR_HOSTNAME"],
415 copr.id,
416 copr.webhook_secret)
417
418 custom_url = "https://{}/webhooks/custom/{}/{}/".format(
419 app.config["PUBLIC_COPR_HOSTNAME"],
420 copr.id,
421 copr.webhook_secret) + "<PACKAGE_NAME>/"
422
423 return flask.render_template(
424 "coprs/detail/settings/integrations.html",
425 copr=copr, bitbucket_url=bitbucket_url, github_url=github_url,
426 gitlab_url=gitlab_url, custom_url=custom_url, pagure_form=pagure_form)
427
428
429 @coprs_ns.route("/<username>/<coprname>/integrations/")
430 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/")
431 @login_required
432 @req_with_copr
433 -def copr_integrations(copr):
446
447
448 @coprs_ns.route("/<username>/<coprname>/integrations/update", methods=["POST"])
449 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/update", methods=["POST"])
450 @login_required
451 @req_with_copr
452 -def copr_integrations_update(copr):
469
481
482
483 @coprs_ns.route("/<username>/<coprname>/edit/")
484 @coprs_ns.route("/g/<group_name>/<coprname>/edit/")
485 @login_required
486 @req_with_copr
487 -def copr_edit(copr, form=None):
489
492 if "rpmfusion" in repos:
493 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>.')
494 flask.flash(message, "error")
495
498 copr.name = form.name.data
499 copr.homepage = form.homepage.data
500 copr.contact = form.contact.data
501 copr.repos = form.repos.data.replace("\n", " ")
502 copr.description = form.description.data
503 copr.instructions = form.instructions.data
504 copr.disable_createrepo = form.disable_createrepo.data
505 copr.build_enable_net = form.build_enable_net.data
506 copr.unlisted_on_hp = form.unlisted_on_hp.data
507 copr.follow_fedora_branching = form.follow_fedora_branching.data
508 copr.delete_after_days = form.delete_after_days.data
509 copr.multilib = form.multilib.data
510 copr.module_hotfixes = form.module_hotfixes.data
511 copr.runtime_dependencies = form.runtime_dependencies.data.replace("\n", " ")
512 copr.bootstrap = form.bootstrap.data
513 if flask.g.user.admin:
514 copr.auto_prune = form.auto_prune.data
515 else:
516 copr.auto_prune = True
517
518 try:
519 coprs_logic.CoprChrootsLogic.update_from_names(
520 flask.g.user, copr, form.selected_chroots)
521
522 coprs_logic.CoprsLogic.update(flask.g.user, copr)
523 except (exceptions.ActionInProgressException,
524 exceptions.InsufficientRightsException,
525 exceptions.ConflictingRequest) as e:
526
527 flask.flash(str(e), "error")
528 db.session.rollback()
529 else:
530 flask.flash("Project has been updated successfully.", "success")
531 db.session.commit()
532
533 copr_deps, _, non_existing = get_transitive_runtime_dependencies(copr)
534 deps_without_chroots = {}
535 for copr_dep in copr_deps:
536 for chroot in copr.active_chroots:
537 if chroot not in copr_dep.active_chroots:
538 if copr_dep in deps_without_chroots:
539 deps_without_chroots[copr_dep].append(chroot.name)
540 else:
541 deps_without_chroots[copr_dep] = [chroot.name]
542
543 if non_existing:
544 flask.flash("Non-existing projects set as runtime dependencies: "
545 "{0}.".format(", ".join(non_existing)), "warning")
546 for dep in deps_without_chroots:
547 flask.flash("Project {0}/{1} that is set as a dependency doesn't "
548 "provide all the chroots enabled in this project: {2}."
549 .format(
550 dep.owner.name if isinstance(dep.owner, models.User)
551 else "@" + dep.owner.name,
552 dep.name, ", ".join(deps_without_chroots[dep])),
553 "warning")
554
555 _check_rpmfusion(copr.repos)
556
557
558 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
559 @coprs_ns.route("/g/<group_name>/<coprname>/update/", methods=["POST"])
560 @login_required
561 @req_with_copr
562 -def copr_update(copr):
570
571
572 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/",
573 methods=["POST"])
574 @coprs_ns.route("/g/<group_name>/<coprname>/permissions_applier_change/", methods=["POST"])
575 @login_required
576 @req_with_copr
577 -def copr_permissions_applier_change(copr):
578 permission = coprs_logic.CoprPermissionsLogic.get(copr, flask.g.user).first()
579 applier_permissions_form = \
580 forms.PermissionsApplierFormFactory.create_form_cls(permission)()
581
582 if copr.user == flask.g.user:
583 flask.flash("Owner cannot request permissions for his own project.", "error")
584 elif applier_permissions_form.validate_on_submit():
585
586 if permission is not None:
587 old_builder = permission.copr_builder
588 old_admin = permission.copr_admin
589 else:
590 old_builder = 0
591 old_admin = 0
592 new_builder = applier_permissions_form.copr_builder.data
593 new_admin = applier_permissions_form.copr_admin.data
594 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier(
595 flask.g.user, copr, permission, new_builder, new_admin)
596 db.session.commit()
597 flask.flash(
598 "Successfully updated permissions for project '{0}'."
599 .format(copr.name))
600
601
602 if flask.current_app.config.get("SEND_EMAILS", False):
603 for mail in copr.admin_mails:
604 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
605 "new_builder": new_builder, "new_admin": new_admin}
606 msg = PermissionRequestMessage(copr, flask.g.user, permission_dict)
607 send_mail([mail], msg)
608
609 return flask.redirect(helpers.copr_url("coprs_ns.copr_detail", copr))
610
611
612 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
613 @coprs_ns.route("/g/<group_name>/<coprname>/update_permissions/", methods=["POST"])
614 @login_required
615 @req_with_copr
616 -def copr_update_permissions(copr):
617 permissions = copr.copr_permissions
618 permissions_form = forms.PermissionsFormFactory.create_form_cls(
619 permissions)()
620
621 if permissions_form.validate_on_submit():
622
623 try:
624
625
626 permissions.sort(
627 key=lambda x: -1 if x.user_id == flask.g.user.id else 1)
628 for perm in permissions:
629 old_builder = perm.copr_builder
630 old_admin = perm.copr_admin
631 new_builder = permissions_form[
632 "copr_builder_{0}".format(perm.user_id)].data
633 new_admin = permissions_form[
634 "copr_admin_{0}".format(perm.user_id)].data
635 coprs_logic.CoprPermissionsLogic.update_permissions(
636 flask.g.user, copr, perm, new_builder, new_admin)
637 if flask.current_app.config.get("SEND_EMAILS", False) and \
638 (old_builder is not new_builder or old_admin is not new_admin):
639 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
640 "new_builder": new_builder, "new_admin": new_admin}
641 msg = PermissionChangeMessage(copr, permission_dict)
642 send_mail(perm.user.mail, msg)
643
644
645 except exceptions.InsufficientRightsException as e:
646 db.session.rollback()
647 flask.flash(str(e), "error")
648 else:
649 db.session.commit()
650 flask.flash("Project permissions were updated successfully.", "success")
651
652 return flask.redirect(url_for_copr_details(copr))
653
654
655 @coprs_ns.route("/<username>/<coprname>/repositories/")
656 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/")
657 @login_required
658 @req_with_copr
659 -def copr_repositories(copr):
665
671
672
673 @coprs_ns.route("/<username>/<coprname>/repositories/", methods=["POST"])
674 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/", methods=["POST"])
675 @login_required
676 @req_with_copr
677 -def copr_repositories_post(copr):
679
711
712
713 @coprs_ns.route("/id/<copr_id>/createrepo/", methods=["POST"])
714 @login_required
715 -def copr_createrepo(copr_id):
727
730 form = forms.CoprDeleteForm()
731 if form.validate_on_submit():
732
733 try:
734 ComplexLogic.delete_copr(copr)
735 except (exceptions.ActionInProgressException,
736 exceptions.InsufficientRightsException) as e:
737
738 db.session.rollback()
739 flask.flash(str(e), "error")
740 return flask.redirect(url_on_error)
741 else:
742 db.session.commit()
743 flask.flash("Project has been deleted successfully.")
744 return flask.redirect(url_on_success)
745 else:
746 return render_template("coprs/detail/settings/delete.html", form=form, copr=copr)
747
748
749 @coprs_ns.route("/<username>/<coprname>/delete/", methods=["GET", "POST"])
750 @coprs_ns.route("/g/<group_name>/<coprname>/delete/", methods=["GET", "POST"])
751 @login_required
752 @req_with_copr
753 -def copr_delete(copr):
760
761
762 @coprs_ns.route("/<username>/<coprname>/legal_flag/", methods=["POST"])
763 @coprs_ns.route("/g/<group_name>/<coprname>/legal_flag/", methods=["POST"])
764 @login_required
765 @req_with_copr
766 -def copr_legal_flag(copr):
768
786
789 """Get a list of runtime dependencies (build transitively from
790 dependencies' dependencies). Returns three lists, one with Copr
791 dependencies, one with list of non-existing Copr dependencies
792 and one with URLs to external dependencies.
793
794 :type copr: models.Copr
795 :rtype: List[models.Copr], List[str], List[str]
796 """
797
798 if not copr:
799 return [], [], []
800
801 wlist = WorkList([copr])
802 internal_deps = set()
803 non_existing = set()
804 external_deps = set()
805
806 while not wlist.empty:
807 analyzed_copr = wlist.pop()
808
809 for dep in analyzed_copr.runtime_deps:
810 try:
811 copr_dep = ComplexLogic.get_copr_by_repo_safe(dep)
812 except ObjectNotFound:
813 non_existing.add(dep)
814 continue
815
816 if not copr_dep:
817 external_deps.add(dep)
818 continue
819 if copr == copr_dep:
820 continue
821
822 internal_deps.add(copr_dep)
823 wlist.schedule(copr_dep)
824
825 return list(internal_deps), list(external_deps), list(non_existing)
826
827
828 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
829 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/<repofile>")
830 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
831 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/<repofile>")
832 @req_with_copr_dir
833 -def generate_repo_file(copr_dir, name_release, repofile):
839
840
841 -def render_repo_template(copr_dir, mock_chroot, arch=None, cost=None, runtime_dep=None, dependent=None):
842 repo_id = "{0}:{1}:{2}:{3}{4}".format(
843 "coprdep" if runtime_dep else "copr",
844 app.config["PUBLIC_COPR_HOSTNAME"].split(":")[0],
845 copr_dir.copr.owner_name.replace("@", "group_"),
846 copr_dir.name,
847 ":ml" if arch else ""
848 )
849
850 if runtime_dep and dependent:
851 name = "Copr {0}/{1}/{2} runtime dependency #{3} - {4}/{5}".format(
852 app.config["PUBLIC_COPR_HOSTNAME"].split(":")[0],
853 dependent.copr.owner_name, dependent.name, runtime_dep,
854 copr_dir.copr.owner_name, copr_dir.name
855 )
856 else:
857 name = "Copr repo for {0} owned by {1}".format(copr_dir.name, copr_dir.copr.owner_name)
858
859 url = os.path.join(copr_dir.repo_url, '')
860 repo_url = generate_repo_url(mock_chroot, url, arch)
861 pubkey_url = urljoin(url, "pubkey.gpg")
862
863 return flask.render_template("coprs/copr_dir.repo", copr_dir=copr_dir,
864 url=repo_url, pubkey_url=pubkey_url,
865 repo_id=repo_id, arch=arch, cost=cost,
866 name=name)
867
881
885 copr = copr_dir.copr
886
887
888 if not any([ch.name.startswith(name_release) for ch in copr.active_chroots]):
889 name_release = app.config["CHROOT_NAME_RELEASE_ALIAS"].get(name_release, name_release)
890
891
892 searched_chroot = name_release if not arch else name_release + "-" + arch
893
894 mock_chroot = None
895 for mc in copr.active_chroots:
896 if not mc.name.startswith(searched_chroot):
897 continue
898 mock_chroot = mc
899
900 if not mock_chroot:
901 raise ObjectNotFound("Chroot {} does not exist in {}".format(
902 searched_chroot, copr.full_name))
903
904
905
906 multilib_on = (arch and
907 copr.multilib and
908 mock_chroot in copr.active_multilib_chroots)
909
910
911 response_content = render_repo_template(copr_dir, mock_chroot)
912
913 if multilib_on:
914
915 response_content += "\n" + render_repo_template(
916 copr_dir, mock_chroot,
917 models.MockChroot.multilib_pairs[mock_chroot.arch],
918 cost=1100)
919
920 internal_deps, external_deps, non_existing = get_transitive_runtime_dependencies(copr)
921 dep_idx = 1
922
923 for runtime_dep in internal_deps:
924 owner_name = runtime_dep.owner.name
925 if isinstance(runtime_dep.owner, models.Group):
926 owner_name = "@{0}".format(owner_name)
927 copr_dep_dir = ComplexLogic.get_copr_dir_safe(owner_name, runtime_dep.name)
928 response_content += "\n" + render_repo_template(copr_dep_dir, mock_chroot,
929 runtime_dep=dep_idx,
930 dependent=copr_dir)
931 dep_idx += 1
932
933 dep_idx = 1
934 for runtime_dep in external_deps:
935 response_content += "\n" + _render_external_repo_template(runtime_dep, copr_dir,
936 mock_chroot, dep_idx)
937 dep_idx += 1
938
939 for dep in non_existing:
940 response_content += (
941 "\n\n# This repository is configured to have a runtime dependency "
942 "on a Copr project {0} but that doesn't exist.".format(dep[7:])
943 )
944
945 response = flask.make_response(response_content)
946
947 response.mimetype = "text/plain"
948 response.headers["Content-Disposition"] = \
949 "filename={0}.repo".format(copr_dir.repo_name)
950
951 name = REPO_DL_STAT_FMT.format(**{
952 'copr_user': copr_dir.copr.user.name,
953 'copr_project_name': copr_dir.copr.name,
954 'copr_name_release': name_release,
955 })
956 CounterStatLogic.incr(name=name, counter_type=CounterStatType.REPO_DL)
957 db.session.commit()
958
959 return response
960
961
962
963
964
965
966 @coprs_ns.route("/<username>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
967 @coprs_ns.route("/g/<group_name>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
968 @req_with_copr
969 -def generate_module_repo_file(copr, name_release, module_nsv):
972
974 module = ModulesLogic.get_by_nsv_str(copr, module_nsv).one()
975 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
976 url = os.path.join(copr.main_dir.repo_url, '')
977 repo_url = generate_repo_url(mock_chroot, copr.modules_url)
978 baseurl = "{}+{}/latest/$basearch".format(repo_url.rstrip("/"), module_nsv)
979 pubkey_url = urljoin(url, "pubkey.gpg")
980 response = flask.make_response(
981 flask.render_template("coprs/copr-modules.cfg", copr=copr, module=module,
982 baseurl=baseurl, pubkey_url=pubkey_url))
983 response.mimetype = "text/plain"
984 response.headers["Content-Disposition"] = \
985 "filename={0}.cfg".format(copr.repo_name)
986 return response
987
988
989
990 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>")
991 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
992 try:
993 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages")
994 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm:
995 response = flask.make_response(rpm.read())
996 response.mimetype = "application/x-rpm"
997 response.headers["Content-Disposition"] = \
998 "filename={0}".format(rpmfile)
999 return response
1000 except IOError:
1001 return flask.render_template("404.html")
1002
1018
1019
1020 @coprs_ns.route("/<username>/<coprname>/monitor/")
1021 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>")
1022 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/")
1023 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>")
1024 @req_with_copr
1025 -def copr_build_monitor(copr, detailed=False):
1027
1028
1029 @coprs_ns.route("/<username>/<coprname>/fork/")
1030 @coprs_ns.route("/g/<group_name>/<coprname>/fork/")
1031 @login_required
1032 @req_with_copr
1033 -def copr_fork(copr):
1036
1039 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
1040
1041
1042 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"])
1043 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
1044 @login_required
1045 @req_with_copr
1046 -def copr_fork_post(copr):
1047 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)()
1048 if form.validate_on_submit():
1049 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0]
1050 if flask.g.user.name != form.owner.data and not dstgroup:
1051 return generic_error("There is no such group: {}".format(form.owner.data))
1052
1053 dst_copr = CoprsLogic.get(flask.g.user.name, form.name.data).all()
1054 if dst_copr and not form.confirm.data:
1055 return render_copr_fork(copr, form, confirm=True)
1056
1057 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data,
1058 dstgroup=dstgroup)
1059
1060 if created:
1061 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes "
1062 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
1063 else:
1064 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes "
1065 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
1066
1067 db.session.commit()
1068 flask.flash(msg)
1069
1070 return flask.redirect(url_for_copr_details(fcopr))
1071 return render_copr_fork(copr, form)
1072
1073
1074 @coprs_ns.route("/<username>/<coprname>/forks/")
1075 @coprs_ns.route("/g/<group_name>/<coprname>/forks/")
1076 @req_with_copr
1077 -def copr_forks(copr):
1078 return flask.render_template("coprs/detail/forks.html", copr=copr)
1079
1083 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update-indexes-quick', '1'])
1084 return "OK"
1085
1086
1087 @coprs_ns.route("/<username>/<coprname>/modules/")
1088 @coprs_ns.route("/g/<group_name>/<coprname>/modules/")
1089 @req_with_copr
1090 -def copr_modules(copr):
1092
1097
1098
1099 @coprs_ns.route("/<username>/<coprname>/create_module/")
1100 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/")
1101 @login_required
1102 @req_with_copr
1103 -def copr_create_module(copr):
1106
1115
1116
1117 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"])
1118 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
1119 @login_required
1120 @req_with_copr
1121 -def copr_create_module_post(copr):
1122 form = forms.CreateModuleForm(copr=copr, meta={'csrf': False})
1123 args = [copr, form]
1124 if "add_profile" in flask.request.values:
1125 return add_profile(*args)
1126 if "build_module" in flask.request.values:
1127 return build_module(*args)
1128
1137
1140 if not form.validate_on_submit():
1141
1142 for i in range(2, len(form.profile_names)):
1143 form.profile_pkgs.append_entry()
1144 return render_create_module(copr, form, profiles=len(form.profile_names))
1145
1146 summary = "Module from Copr repository: {}".format(copr.full_name)
1147 generator = ModulemdGenerator(str(copr.name), summary=summary, config=app.config)
1148 generator.add_filter(form.filter.data)
1149 generator.add_api(form.api.data)
1150 generator.add_profiles(enumerate(zip(form.profile_names.data, form.profile_pkgs.data)))
1151 generator.add_components(form.packages.data, form.filter.data, form.builds.data)
1152 yaml = generator.generate()
1153
1154 facade = None
1155 try:
1156 facade = ModuleBuildFacade(flask.g.user, copr, yaml)
1157 module = facade.submit_build()
1158 db.session.commit()
1159
1160 flask.flash("Modulemd yaml file successfully generated and submitted to be build as {}"
1161 .format(module.nsv), "success")
1162 return flask.redirect(url_for_copr_details(copr))
1163
1164 except ValidationError as ex:
1165 flask.flash(ex.message, "error")
1166 return render_create_module(copr, form, len(form.profile_names))
1167
1168 except sqlalchemy.exc.IntegrityError:
1169 flask.flash("Module {}-{}-{} already exists".format(
1170 facade.modulemd.name, facade.modulemd.stream, facade.modulemd.version), "error")
1171 db.session.rollback()
1172 return render_create_module(copr, form, len(form.profile_names))
1173
1174
1175 @coprs_ns.route("/<username>/<coprname>/module/<id>")
1176 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>")
1177 @req_with_copr
1178 -def copr_module(copr, id):
1196
1197
1198 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw")
1199 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw")
1200 @req_with_copr
1201 -def copr_module_raw(copr, id):
1208