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 copr_common.enums import StatusEnum
23 from coprs import app
24 from coprs import cache
25 from coprs import db
26 from coprs import rcp
27 from coprs import exceptions
28 from coprs import forms
29 from coprs import helpers
30 from coprs import models
31 from coprs.exceptions import ObjectNotFound
32 from coprs.logic.coprs_logic import CoprsLogic, PinnedCoprsLogic, MockChrootsLogic
33 from coprs.logic.stat_logic import CounterStatLogic
34 from coprs.logic.modules_logic import ModulesLogic, ModulemdGenerator, ModuleBuildFacade
35 from coprs.rmodels import TimedStatEvents
36 from coprs.mail import send_mail, LegalFlagMessage, PermissionRequestMessage, PermissionChangeMessage
37
38 from coprs.logic.complex_logic import ComplexLogic
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
54
61
62
63 @coprs_ns.route("/", defaults={"page": 1})
64 @coprs_ns.route("/<int:page>/")
65 -def coprs_show(page=1):
66 query = CoprsLogic.get_multiple(include_unlisted_on_hp=False)
67 query = CoprsLogic.set_query_order(query, desc=True)
68
69 paginator = helpers.Paginator(query, query.count(), page)
70
71 coprs = paginator.sliced_query
72
73
74
75
76 users_builds = builds_logic.BuildsLogic.get_recent_tasks(None, 4)
77
78 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
79
80 return flask.render_template("coprs/show/all.html",
81 coprs=coprs,
82 pinned=[],
83 paginator=paginator,
84 tasks_info=ComplexLogic.get_queue_sizes(),
85 users_builds=users_builds,
86 graph=data)
87
88
89 @coprs_ns.route("/<username>/", defaults={"page": 1})
90 @coprs_ns.route("/<username>/<int:page>/")
91 -def coprs_by_user(username=None, page=1):
92 user = users_logic.UsersLogic.get(username).first()
93 if not user:
94 return page_not_found(
95 "User {0} does not exist.".format(username))
96
97 pinned = [pin.copr for pin in PinnedCoprsLogic.get_by_user_id(user.id)] if page == 1 else []
98 query = CoprsLogic.get_multiple_owned_by_username(username)
99 query = CoprsLogic.filter_without_ids(query, [copr.id for copr in pinned])
100 query = CoprsLogic.filter_without_group_projects(query)
101 query = CoprsLogic.set_query_order(query, desc=True)
102
103 paginator = helpers.Paginator(query, query.count(), page)
104 coprs = paginator.sliced_query
105
106
107 users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 4)
108
109 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
110
111 return flask.render_template("coprs/show/user.html",
112 user=user,
113 coprs=coprs,
114 pinned=pinned,
115 paginator=paginator,
116 tasks_info=ComplexLogic.get_queue_sizes(),
117 users_builds=users_builds,
118 graph=data)
119
120
121 @coprs_ns.route("/fulltext/", defaults={"page": 1})
122 @coprs_ns.route("/fulltext/<int:page>/")
123 -def coprs_fulltext_search(page=1):
124 fulltext = flask.request.args.get("fulltext", "")
125 try:
126 query = coprs_logic.CoprsLogic.get_multiple_fulltext(fulltext)
127 except ValueError as e:
128 flask.flash(str(e), "error")
129 return flask.redirect(flask.request.referrer or
130 flask.url_for("coprs_ns.coprs_show"))
131
132 paginator = helpers.Paginator(query, query.count(), page,
133 additional_params={"fulltext": fulltext})
134
135 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
136
137 coprs = paginator.sliced_query
138 return render_template("coprs/show/fulltext.html",
139 coprs=coprs,
140 pinned=[],
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):
159
160
161 @coprs_ns.route("/<username>/new/", methods=["POST"])
162 @coprs_ns.route("/g/<group_name>/new/", methods=["POST"])
163 @login_required
164 -def copr_new(username=None, group_name=None):
165 """
166 Receive information from the user (and group) on how to create its new copr
167 and create it accordingly.
168 """
169 group = None
170 redirect = "coprs/add.html"
171 if group_name:
172 group = ComplexLogic.get_group_by_name_safe(group_name)
173 redirect = "coprs/group_add.html"
174
175 form = forms.CoprFormFactory.create_form_cls(group=group)()
176 if form.validate_on_submit():
177 try:
178 copr = coprs_logic.CoprsLogic.add(
179 flask.g.user,
180 name=form.name.data,
181 homepage=form.homepage.data,
182 contact=form.contact.data,
183 repos=form.repos.data.replace("\n", " "),
184 selected_chroots=form.selected_chroots,
185 description=form.description.data,
186 instructions=form.instructions.data,
187 disable_createrepo=form.disable_createrepo.data,
188 build_enable_net=form.build_enable_net.data,
189 unlisted_on_hp=form.unlisted_on_hp.data,
190 group=group,
191 persistent=form.persistent.data,
192 auto_prune=(form.auto_prune.data if flask.g.user.admin else True),
193 use_bootstrap_container=form.use_bootstrap_container.data,
194 follow_fedora_branching=form.follow_fedora_branching.data,
195 delete_after_days=form.delete_after_days.data,
196 multilib=form.multilib.data,
197 )
198
199 db.session.commit()
200 after_the_project_creation(copr, form)
201 return flask.redirect(url_for_copr_details(copr))
202 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e:
203 flask.flash(str(e), "error")
204
205 return flask.render_template(redirect, form=form, group=group)
206
209 flask.flash("New project has been created successfully.", "success")
210 _check_rpmfusion(copr.repos)
211 if form.initial_pkgs.data:
212 pkgs = form.initial_pkgs.data.replace("\n", " ").split(" ")
213
214
215 bad_urls = []
216 for pkg in pkgs:
217 if not pkg.endswith(".src.rpm"):
218 bad_urls.append(pkg)
219 flask.flash("Bad url: {0} (skipped)".format(pkg))
220 for bad_url in bad_urls:
221 pkgs.remove(bad_url)
222
223 if not pkgs:
224 flask.flash("No initial packages submitted")
225 else:
226
227 for pkg in pkgs:
228 builds_logic.BuildsLogic.add(
229 flask.g.user,
230 pkgs=pkg,
231 srpm_url=pkg,
232 copr=copr,
233 enable_net=form.build_enable_net.data
234 )
235
236 db.session.commit()
237 flask.flash("Initial packages were successfully submitted "
238 "for building.")
239
240
241 @coprs_ns.route("/<username>/<coprname>/report-abuse")
242 @coprs_ns.route("/g/<group_name>/<coprname>/report-abuse")
243 @req_with_copr
244 @login_required
245 -def copr_report_abuse(copr):
247
252
253
254 @coprs_ns.route("/<username>/<coprname>/")
255 @coprs_ns.route("/g/<group_name>/<coprname>/")
256 @req_with_copr
257 -def copr_detail(copr):
259
262 repo_dl_stat = CounterStatLogic.get_copr_repo_dl_stat(copr)
263 form = forms.CoprLegalFlagForm()
264 repos_info = {}
265 for chroot in copr.active_chroots:
266 chroot_rpms_dl_stat_key = CHROOT_RPMS_DL_STAT_FMT.format(
267 copr_user=copr.owner_name,
268 copr_project_name=copr.name,
269 copr_chroot=chroot.name,
270 )
271 chroot_rpms_dl_stat = TimedStatEvents.get_count(
272 rconnect=rcp.get_connection(),
273 name=chroot_rpms_dl_stat_key,
274 )
275
276 logoset = set()
277 logodir = app.static_folder + "/chroot_logodir"
278 for logo in os.listdir(logodir):
279
280 if fnmatch.fnmatch(logo, "*.png"):
281 logoset.add(logo[:-4])
282
283 if chroot.name_release not in repos_info:
284 logo = None
285 if chroot.name_release in logoset:
286 logo = chroot.name_release + ".png"
287 elif chroot.os_release in logoset:
288 logo = chroot.os_release + ".png"
289
290 repos_info[chroot.name_release] = {
291 "name_release": chroot.name_release,
292 "os_release": chroot.os_release,
293 "os_version": chroot.os_version,
294 "logo": logo,
295 "arch_list": [chroot.arch],
296 "repo_file": "{}-{}.repo".format(copr.repo_id, chroot.name_release),
297 "dl_stat": repo_dl_stat[chroot.name_release],
298 "rpm_dl_stat": {
299 chroot.arch: chroot_rpms_dl_stat
300 }
301 }
302 else:
303 repos_info[chroot.name_release]["arch_list"].append(chroot.arch)
304 repos_info[chroot.name_release]["rpm_dl_stat"][chroot.arch] = chroot_rpms_dl_stat
305
306 if copr.multilib:
307 for name_release in repos_info:
308 arches = repos_info[name_release]['arch_list']
309 arch_repos = {}
310 for ch64, ch32 in models.MockChroot.multilib_pairs.items():
311 if set([ch64, ch32]).issubset(set(arches)):
312 arch_repos[ch64] = ch32
313
314 repos_info[name_release]['arch_repos'] = arch_repos
315
316
317 repos_info_list = sorted(repos_info.values(), key=lambda rec: rec["name_release"])
318 builds = builds_logic.BuildsLogic.get_multiple_by_copr(copr=copr).limit(1).all()
319
320 return flask.render_template(
321 "coprs/detail/overview.html",
322 copr=copr,
323 user=flask.g.user,
324 form=form,
325 repo_dl_stat=repo_dl_stat,
326 repos_info_list=repos_info_list,
327 latest_build=builds[0] if len(builds) == 1 else None,
328 )
329
330
331 @coprs_ns.route("/<username>/<coprname>/permissions/")
332 @coprs_ns.route("/g/<group_name>/<coprname>/permissions/")
333 @req_with_copr
334 -def copr_permissions(copr):
362
365 if not copr.webhook_secret:
366 copr.new_webhook_secret()
367 db.session.add(copr)
368 db.session.commit()
369
370 bitbucket_url = "https://{}/webhooks/bitbucket/{}/{}/".format(
371 app.config["PUBLIC_COPR_HOSTNAME"],
372 copr.id,
373 copr.webhook_secret)
374
375 github_url = "https://{}/webhooks/github/{}/{}/".format(
376 app.config["PUBLIC_COPR_HOSTNAME"],
377 copr.id,
378 copr.webhook_secret)
379
380 gitlab_url = "https://{}/webhooks/gitlab/{}/{}/".format(
381 app.config["PUBLIC_COPR_HOSTNAME"],
382 copr.id,
383 copr.webhook_secret)
384
385 custom_url = "https://{}/webhooks/custom/{}/{}/".format(
386 app.config["PUBLIC_COPR_HOSTNAME"],
387 copr.id,
388 copr.webhook_secret) + "<PACKAGE_NAME>/"
389
390 return flask.render_template(
391 "coprs/detail/settings/integrations.html",
392 copr=copr, bitbucket_url=bitbucket_url, github_url=github_url,
393 gitlab_url=gitlab_url, custom_url=custom_url, pagure_form=pagure_form)
394
395
396 @coprs_ns.route("/<username>/<coprname>/integrations/")
397 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/")
398 @login_required
399 @req_with_copr
400 -def copr_integrations(copr):
413
414
415 @coprs_ns.route("/<username>/<coprname>/integrations/update", methods=["POST"])
416 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/update", methods=["POST"])
417 @login_required
418 @req_with_copr
419 -def copr_integrations_update(copr):
436
448
449
450 @coprs_ns.route("/<username>/<coprname>/edit/")
451 @coprs_ns.route("/g/<group_name>/<coprname>/edit/")
452 @login_required
453 @req_with_copr
454 -def copr_edit(copr, form=None):
456
459 if "rpmfusion" in repos:
460 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>.')
461 flask.flash(message, "error")
462
497
498
499 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
500 @coprs_ns.route("/g/<group_name>/<coprname>/update/", methods=["POST"])
501 @login_required
502 @req_with_copr
503 -def copr_update(copr):
511
512
513 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/",
514 methods=["POST"])
515 @coprs_ns.route("/g/<group_name>/<coprname>/permissions_applier_change/", methods=["POST"])
516 @login_required
517 @req_with_copr
518 -def copr_permissions_applier_change(copr):
519 permission = coprs_logic.CoprPermissionsLogic.get(copr, flask.g.user).first()
520 applier_permissions_form = \
521 forms.PermissionsApplierFormFactory.create_form_cls(permission)()
522
523 if copr.user == flask.g.user:
524 flask.flash("Owner cannot request permissions for his own project.", "error")
525 elif applier_permissions_form.validate_on_submit():
526
527 if permission is not None:
528 old_builder = permission.copr_builder
529 old_admin = permission.copr_admin
530 else:
531 old_builder = 0
532 old_admin = 0
533 new_builder = applier_permissions_form.copr_builder.data
534 new_admin = applier_permissions_form.copr_admin.data
535 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier(
536 flask.g.user, copr, permission, new_builder, new_admin)
537 db.session.commit()
538 flask.flash(
539 "Successfully updated permissions for project '{0}'."
540 .format(copr.name))
541
542
543 if flask.current_app.config.get("SEND_EMAILS", False):
544 for mail in copr.admin_mails:
545 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
546 "new_builder": new_builder, "new_admin": new_admin}
547 msg = PermissionRequestMessage(copr, flask.g.user, permission_dict)
548 send_mail([mail], msg)
549
550 return flask.redirect(helpers.copr_url("coprs_ns.copr_detail", copr))
551
552
553 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
554 @coprs_ns.route("/g/<group_name>/<coprname>/update_permissions/", methods=["POST"])
555 @login_required
556 @req_with_copr
557 -def copr_update_permissions(copr):
558 permissions = copr.copr_permissions
559 permissions_form = forms.PermissionsFormFactory.create_form_cls(
560 permissions)()
561
562 if permissions_form.validate_on_submit():
563
564 try:
565
566
567 permissions.sort(
568 key=lambda x: -1 if x.user_id == flask.g.user.id else 1)
569 for perm in permissions:
570 old_builder = perm.copr_builder
571 old_admin = perm.copr_admin
572 new_builder = permissions_form[
573 "copr_builder_{0}".format(perm.user_id)].data
574 new_admin = permissions_form[
575 "copr_admin_{0}".format(perm.user_id)].data
576 coprs_logic.CoprPermissionsLogic.update_permissions(
577 flask.g.user, copr, perm, new_builder, new_admin)
578 if flask.current_app.config.get("SEND_EMAILS", False) and \
579 (old_builder is not new_builder or old_admin is not new_admin):
580 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
581 "new_builder": new_builder, "new_admin": new_admin}
582 msg = PermissionChangeMessage(copr, permission_dict)
583 send_mail(perm.user.mail, msg)
584
585
586 except exceptions.InsufficientRightsException as e:
587 db.session.rollback()
588 flask.flash(str(e), "error")
589 else:
590 db.session.commit()
591 flask.flash("Project permissions were updated successfully.", "success")
592
593 return flask.redirect(url_for_copr_details(copr))
594
595
596 @coprs_ns.route("/<username>/<coprname>/repositories/")
597 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/")
598 @login_required
599 @req_with_copr
600 -def copr_repositories(copr):
606
612
613
614 @coprs_ns.route("/<username>/<coprname>/repositories/", methods=["POST"])
615 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/", methods=["POST"])
616 @login_required
617 @req_with_copr
618 -def copr_repositories_post(copr):
619 if not flask.g.user.can_edit(copr):
620 flask.flash("You don't have access to this page.", "error")
621 return flask.redirect(url_for_copr_details(copr))
622
623 form = forms.CoprChrootExtend()
624 if form.extend.data:
625 delete_after_days = app.config["DELETE_EOL_CHROOTS_AFTER"] + 1
626 chroot_name = form.extend.data
627 flask.flash("Repository for {} will be preserved for another {} days from now"
628 .format(chroot_name, app.config["DELETE_EOL_CHROOTS_AFTER"]))
629 elif form.expire.data:
630 delete_after_days = 0
631 chroot_name = form.expire.data
632 flask.flash("Repository for {} is scheduled to be removed."
633 "If you changed your mind, click 'Extend` to revert your decision."
634 .format(chroot_name))
635 else:
636 raise ValidationError("Copr chroot needs to be either extended or expired")
637
638 copr_chroot = coprs_logic.CoprChrootsLogic.get_by_name(copr, chroot_name, active_only=False).one()
639 delete_after_timestamp = datetime.datetime.now() + datetime.timedelta(days=delete_after_days)
640 coprs_logic.CoprChrootsLogic.update_chroot(flask.g.user, copr_chroot,
641 delete_after=delete_after_timestamp)
642 db.session.commit()
643 return render_copr_repositories(copr)
644
645
646 @coprs_ns.route("/id/<copr_id>/createrepo/", methods=["POST"])
647 @login_required
648 -def copr_createrepo(copr_id):
660
663 form = forms.CoprDeleteForm()
664 if form.validate_on_submit():
665
666 try:
667 ComplexLogic.delete_copr(copr)
668 except (exceptions.ActionInProgressException,
669 exceptions.InsufficientRightsException) as e:
670
671 db.session.rollback()
672 flask.flash(str(e), "error")
673 return flask.redirect(url_on_error)
674 else:
675 db.session.commit()
676 flask.flash("Project has been deleted successfully.")
677 return flask.redirect(url_on_success)
678 else:
679 return render_template("coprs/detail/settings/delete.html", form=form, copr=copr)
680
681
682 @coprs_ns.route("/<username>/<coprname>/delete/", methods=["GET", "POST"])
683 @coprs_ns.route("/g/<group_name>/<coprname>/delete/", methods=["GET", "POST"])
684 @login_required
685 @req_with_copr
686 -def copr_delete(copr):
693
694
695 @coprs_ns.route("/<username>/<coprname>/legal_flag/", methods=["POST"])
696 @coprs_ns.route("/g/<group_name>/<coprname>/legal_flag/", methods=["POST"])
697 @login_required
698 @req_with_copr
699 -def copr_legal_flag(copr):
701
719
720
721 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
722 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/<repofile>")
723 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
724 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/<repofile>")
725 @req_with_copr_dir
726 -def generate_repo_file(copr_dir, name_release, repofile):
732
735 repo_id = "copr:{0}:{1}:{2}{3}".format(
736 app.config["PUBLIC_COPR_HOSTNAME"].split(":")[0],
737 copr_dir.copr.owner_name.replace("@", "group_"),
738 copr_dir.name,
739 ":ml" if arch else ""
740 )
741 url = os.path.join(copr_dir.repo_url, '')
742 repo_url = generate_repo_url(mock_chroot, url, arch)
743 pubkey_url = urljoin(url, "pubkey.gpg")
744 return flask.render_template("coprs/copr_dir.repo", copr_dir=copr_dir,
745 url=repo_url, pubkey_url=pubkey_url,
746 repo_id=repo_id, arch=arch, cost=cost) + "\n"
747
751 copr = copr_dir.copr
752
753
754 if not any([ch.name.startswith(name_release) for ch in copr.active_chroots]):
755 name_release = app.config["CHROOT_NAME_RELEASE_ALIAS"].get(name_release, name_release)
756
757
758 searched_chroot = name_release if not arch else name_release + "-" + arch
759
760 mock_chroot = None
761 for mc in copr.active_chroots:
762 if not mc.name.startswith(searched_chroot):
763 continue
764 mock_chroot = mc
765
766 if not mock_chroot:
767 raise ObjectNotFound("Chroot {} does not exist in {}".format(
768 searched_chroot, copr.full_name))
769
770
771
772 multilib_on = (arch and
773 copr.multilib and
774 mock_chroot in copr.active_multilib_chroots)
775
776
777 response_content = render_repo_template(copr_dir, mock_chroot)
778
779 if multilib_on:
780
781 response_content += "\n" + render_repo_template(
782 copr_dir, mock_chroot,
783 models.MockChroot.multilib_pairs[mock_chroot.arch],
784 cost=1100)
785
786 response = flask.make_response(response_content)
787
788 response.mimetype = "text/plain"
789 response.headers["Content-Disposition"] = \
790 "filename={0}.repo".format(copr_dir.repo_name)
791
792 name = REPO_DL_STAT_FMT.format(**{
793 'copr_user': copr_dir.copr.user.name,
794 'copr_project_name': copr_dir.copr.name,
795 'copr_name_release': name_release,
796 })
797 CounterStatLogic.incr(name=name, counter_type=CounterStatType.REPO_DL)
798 db.session.commit()
799
800 return response
801
802
803
804
805
806
807 @coprs_ns.route("/<username>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
808 @coprs_ns.route("/g/<group_name>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
809 @req_with_copr
810 -def generate_module_repo_file(copr, name_release, module_nsv):
813
815 module = ModulesLogic.get_by_nsv_str(copr, module_nsv).one()
816 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
817 url = os.path.join(copr.main_dir.repo_url, '')
818 repo_url = generate_repo_url(mock_chroot, copr.modules_url)
819 baseurl = "{}+{}/latest/$basearch".format(repo_url.rstrip("/"), module_nsv)
820 pubkey_url = urljoin(url, "pubkey.gpg")
821 response = flask.make_response(
822 flask.render_template("coprs/copr-modules.cfg", copr=copr, module=module,
823 baseurl=baseurl, pubkey_url=pubkey_url))
824 response.mimetype = "text/plain"
825 response.headers["Content-Disposition"] = \
826 "filename={0}.cfg".format(copr.repo_name)
827 return response
828
829
830
831 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>")
832 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
833 try:
834 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages")
835 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm:
836 response = flask.make_response(rpm.read())
837 response.mimetype = "application/x-rpm"
838 response.headers["Content-Disposition"] = \
839 "filename={0}".format(rpmfile)
840 return response
841 except IOError:
842 return flask.render_template("404.html")
843
860
861
862 @coprs_ns.route("/<username>/<coprname>/monitor/")
863 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>")
864 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/")
865 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>")
866 @req_with_copr
867 -def copr_build_monitor(copr, detailed=False):
869
870
871 @coprs_ns.route("/<username>/<coprname>/fork/")
872 @coprs_ns.route("/g/<group_name>/<coprname>/fork/")
873 @login_required
874 @req_with_copr
875 -def copr_fork(copr):
878
881 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
882
883
884 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"])
885 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
886 @login_required
887 @req_with_copr
888 -def copr_fork_post(copr):
889 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)()
890 if form.validate_on_submit():
891 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0]
892 if flask.g.user.name != form.owner.data and not dstgroup:
893 return generic_error("There is no such group: {}".format(form.owner.data))
894
895 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup)
896 if created:
897 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes "
898 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
899 elif not created and form.confirm.data == True:
900 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes "
901 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
902 else:
903 return render_copr_fork(copr, form, confirm=True)
904
905 db.session.commit()
906 flask.flash(msg)
907
908 return flask.redirect(url_for_copr_details(fcopr))
909 return render_copr_fork(copr, form)
910
911
912 @coprs_ns.route("/<username>/<coprname>/forks/")
913 @coprs_ns.route("/g/<group_name>/<coprname>/forks/")
914 @req_with_copr
915 -def copr_forks(copr):
916 return flask.render_template("coprs/detail/forks.html", copr=copr)
917
921 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update-indexes-quick', '1'])
922 return "OK"
923
924
925 @coprs_ns.route("/<username>/<coprname>/modules/")
926 @coprs_ns.route("/g/<group_name>/<coprname>/modules/")
927 @req_with_copr
928 -def copr_modules(copr):
930
935
936
937 @coprs_ns.route("/<username>/<coprname>/create_module/")
938 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/")
939 @login_required
940 @req_with_copr
941 -def copr_create_module(copr):
944
953
954
955 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"])
956 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
957 @login_required
958 @req_with_copr
959 -def copr_create_module_post(copr):
960 form = forms.CreateModuleForm(copr=copr, meta={'csrf': False})
961 args = [copr, form]
962 if "add_profile" in flask.request.values:
963 return add_profile(*args)
964 if "build_module" in flask.request.values:
965 return build_module(*args)
966
975
978 if not form.validate_on_submit():
979
980 for i in range(2, len(form.profile_names)):
981 form.profile_pkgs.append_entry()
982 return render_create_module(copr, form, profiles=len(form.profile_names))
983
984 summary = "Module from Copr repository: {}".format(copr.full_name)
985 generator = ModulemdGenerator(str(copr.name), summary=summary, config=app.config)
986 generator.add_filter(form.filter.data)
987 generator.add_api(form.api.data)
988 generator.add_profiles(enumerate(zip(form.profile_names.data, form.profile_pkgs.data)))
989 generator.add_components(form.packages.data, form.filter.data, form.builds.data)
990 yaml = generator.generate()
991
992 facade = None
993 try:
994 facade = ModuleBuildFacade(flask.g.user, copr, yaml)
995 module = facade.submit_build()
996 db.session.commit()
997
998 flask.flash("Modulemd yaml file successfully generated and submitted to be build as {}"
999 .format(module.nsv), "success")
1000 return flask.redirect(url_for_copr_details(copr))
1001
1002 except ValidationError as ex:
1003 flask.flash(ex.message, "error")
1004 return render_create_module(copr, form, len(form.profile_names))
1005
1006 except sqlalchemy.exc.IntegrityError:
1007 flask.flash("Module {}-{}-{} already exists".format(
1008 facade.modulemd.name, facade.modulemd.stream, facade.modulemd.version), "error")
1009 db.session.rollback()
1010 return render_create_module(copr, form, len(form.profile_names))
1011
1012
1013 @coprs_ns.route("/<username>/<coprname>/module/<id>")
1014 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>")
1015 @req_with_copr
1016 -def copr_module(copr, id):
1034
1035
1036 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw")
1037 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw")
1038 @req_with_copr
1039 -def copr_module_raw(copr, id):
1046