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 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
39 from coprs.views.misc import (login_required, page_not_found, req_with_copr,
40 generic_error, req_with_copr_dir)
41
42 from coprs.views.coprs_ns import coprs_ns
43
44 from coprs.logic import builds_logic, coprs_logic, actions_logic, users_logic
45 from coprs.helpers import generate_repo_url, CHROOT_RPMS_DL_STAT_FMT, \
46 url_for_copr_view, REPO_DL_STAT_FMT, CounterStatType
53
60
61
62 @coprs_ns.route("/", defaults={"page": 1})
63 @coprs_ns.route("/<int:page>/")
64 -def coprs_show(page=1):
65 query = CoprsLogic.get_multiple(include_unlisted_on_hp=False)
66 query = CoprsLogic.set_query_order(query, desc=True)
67
68 paginator = helpers.Paginator(query, query.count(), page)
69
70 coprs = paginator.sliced_query
71
72
73
74
75 users_builds = builds_logic.BuildsLogic.get_recent_tasks(None, 4)
76
77 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
78
79 return flask.render_template("coprs/show/all.html",
80 coprs=coprs,
81 pinned=[],
82 paginator=paginator,
83 tasks_info=ComplexLogic.get_queue_sizes(),
84 users_builds=users_builds,
85 graph=data)
86
87
88 @coprs_ns.route("/<username>/", defaults={"page": 1})
89 @coprs_ns.route("/<username>/<int:page>/")
90 -def coprs_by_user(username=None, page=1):
91 user = users_logic.UsersLogic.get(username).first()
92 if not user:
93 return page_not_found(
94 "User {0} does not exist.".format(username))
95
96 pinned = [pin.copr for pin in PinnedCoprsLogic.get_by_user_id(user.id)] if page == 1 else []
97 query = CoprsLogic.get_multiple_owned_by_username(username)
98 query = CoprsLogic.filter_without_ids(query, [copr.id for copr in pinned])
99 query = CoprsLogic.filter_without_group_projects(query)
100 query = CoprsLogic.set_query_order(query, desc=True)
101
102 paginator = helpers.Paginator(query, query.count(), page)
103 coprs = paginator.sliced_query
104
105
106 users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 4)
107
108 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
109
110 return flask.render_template("coprs/show/user.html",
111 user=user,
112 coprs=coprs,
113 pinned=pinned,
114 paginator=paginator,
115 tasks_info=ComplexLogic.get_queue_sizes(),
116 users_builds=users_builds,
117 graph=data)
118
119
120 @coprs_ns.route("/fulltext/", defaults={"page": 1})
121 @coprs_ns.route("/fulltext/<int:page>/")
122 -def coprs_fulltext_search(page=1):
123 fulltext = flask.request.args.get("fulltext", "")
124 try:
125 query = coprs_logic.CoprsLogic.get_multiple_fulltext(fulltext)
126 except ValueError as e:
127 flask.flash(str(e), "error")
128 return flask.redirect(flask.request.referrer or
129 flask.url_for("coprs_ns.coprs_show"))
130
131 paginator = helpers.Paginator(query, query.count(), page,
132 additional_params={"fulltext": fulltext})
133
134 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
135
136 coprs = paginator.sliced_query
137 return render_template("coprs/show/fulltext.html",
138 coprs=coprs,
139 pinned=[],
140 paginator=paginator,
141 fulltext=fulltext,
142 tasks_info=ComplexLogic.get_queue_sizes(),
143 graph=data)
144
145
146 @coprs_ns.route("/<username>/add/")
147 @coprs_ns.route("/g/<group_name>/add/")
148 @login_required
149 -def copr_add(username=None, group_name=None):
155
156
157 @coprs_ns.route("/<username>/new/", methods=["POST"])
158 @coprs_ns.route("/g/<group_name>/new/", methods=["POST"])
159 @login_required
160 -def copr_new(username=None, group_name=None):
161 """
162 Receive information from the user (and group) on how to create its new copr
163 and create it accordingly.
164 """
165 group = None
166 redirect = "coprs/add.html"
167 if group_name:
168 group = ComplexLogic.get_group_by_name_safe(group_name)
169 redirect = "coprs/group_add.html"
170
171 form = forms.CoprFormFactory.create_form_cls(group=group)()
172 if form.validate_on_submit():
173 try:
174 copr = coprs_logic.CoprsLogic.add(
175 flask.g.user,
176 name=form.name.data,
177 homepage=form.homepage.data,
178 contact=form.contact.data,
179 repos=form.repos.data.replace("\n", " "),
180 selected_chroots=form.selected_chroots,
181 description=form.description.data,
182 instructions=form.instructions.data,
183 disable_createrepo=form.disable_createrepo.data,
184 build_enable_net=form.build_enable_net.data,
185 unlisted_on_hp=form.unlisted_on_hp.data,
186 group=group,
187 persistent=form.persistent.data,
188 auto_prune=(form.auto_prune.data if flask.g.user.admin else True),
189 use_bootstrap_container=form.use_bootstrap_container.data,
190 follow_fedora_branching=form.follow_fedora_branching.data,
191 delete_after_days=form.delete_after_days.data,
192 multilib=form.multilib.data,
193 )
194
195 db.session.commit()
196 after_the_project_creation(copr, form)
197 return flask.redirect(url_for_copr_details(copr))
198 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e:
199 flask.flash(str(e), "error")
200
201 return flask.render_template(redirect, form=form, group=group)
202
205 flask.flash("New project has been created successfully.", "success")
206 _check_rpmfusion(copr.repos)
207 if form.initial_pkgs.data:
208 pkgs = form.initial_pkgs.data.replace("\n", " ").split(" ")
209
210
211 bad_urls = []
212 for pkg in pkgs:
213 if not pkg.endswith(".src.rpm"):
214 bad_urls.append(pkg)
215 flask.flash("Bad url: {0} (skipped)".format(pkg))
216 for bad_url in bad_urls:
217 pkgs.remove(bad_url)
218
219 if not pkgs:
220 flask.flash("No initial packages submitted")
221 else:
222
223 for pkg in pkgs:
224 builds_logic.BuildsLogic.add(
225 flask.g.user,
226 pkgs=pkg,
227 srpm_url=pkg,
228 copr=copr,
229 enable_net=form.build_enable_net.data
230 )
231
232 db.session.commit()
233 flask.flash("Initial packages were successfully submitted "
234 "for building.")
235
236
237 @coprs_ns.route("/<username>/<coprname>/report-abuse")
238 @coprs_ns.route("/g/<group_name>/<coprname>/report-abuse")
239 @req_with_copr
240 @login_required
241 -def copr_report_abuse(copr):
243
248
249
250 @coprs_ns.route("/<username>/<coprname>/")
251 @coprs_ns.route("/g/<group_name>/<coprname>/")
252 @req_with_copr
253 -def copr_detail(copr):
255
258 repo_dl_stat = CounterStatLogic.get_copr_repo_dl_stat(copr)
259 form = forms.CoprLegalFlagForm()
260 repos_info = {}
261 for chroot in copr.active_chroots:
262 chroot_rpms_dl_stat_key = CHROOT_RPMS_DL_STAT_FMT.format(
263 copr_user=copr.owner_name,
264 copr_project_name=copr.name,
265 copr_chroot=chroot.name,
266 )
267 chroot_rpms_dl_stat = TimedStatEvents.get_count(
268 rconnect=rcp.get_connection(),
269 name=chroot_rpms_dl_stat_key,
270 )
271
272 logoset = set()
273 logodir = app.static_folder + "/chroot_logodir"
274 for logo in os.listdir(logodir):
275
276 if fnmatch.fnmatch(logo, "*.png"):
277 logoset.add(logo[:-4])
278
279 if chroot.name_release not in repos_info:
280 logo = None
281 if chroot.name_release in logoset:
282 logo = chroot.name_release + ".png"
283 elif chroot.os_release in logoset:
284 logo = chroot.os_release + ".png"
285
286 repos_info[chroot.name_release] = {
287 "name_release": chroot.name_release,
288 "os_release": chroot.os_release,
289 "os_version": chroot.os_version,
290 "logo": logo,
291 "arch_list": [chroot.arch],
292 "repo_file": "{}-{}.repo".format(copr.repo_id, chroot.name_release),
293 "dl_stat": repo_dl_stat[chroot.name_release],
294 "rpm_dl_stat": {
295 chroot.arch: chroot_rpms_dl_stat
296 }
297 }
298 else:
299 repos_info[chroot.name_release]["arch_list"].append(chroot.arch)
300 repos_info[chroot.name_release]["rpm_dl_stat"][chroot.arch] = chroot_rpms_dl_stat
301
302 if copr.multilib:
303 for name_release in repos_info:
304 arches = repos_info[name_release]['arch_list']
305 arch_repos = {}
306 for ch64, ch32 in models.MockChroot.multilib_pairs.items():
307 if set([ch64, ch32]).issubset(set(arches)):
308 arch_repos[ch64] = ch32
309
310 repos_info[name_release]['arch_repos'] = arch_repos
311
312
313 repos_info_list = sorted(repos_info.values(), key=lambda rec: rec["name_release"])
314 builds = builds_logic.BuildsLogic.get_multiple_by_copr(copr=copr).limit(1).all()
315
316 return flask.render_template(
317 "coprs/detail/overview.html",
318 copr=copr,
319 user=flask.g.user,
320 form=form,
321 repo_dl_stat=repo_dl_stat,
322 repos_info_list=repos_info_list,
323 latest_build=builds[0] if len(builds) == 1 else None,
324 )
325
326
327 @coprs_ns.route("/<username>/<coprname>/permissions/")
328 @coprs_ns.route("/g/<group_name>/<coprname>/permissions/")
329 @req_with_copr
330 -def copr_permissions(copr):
358
361 if not copr.webhook_secret:
362 copr.new_webhook_secret()
363 db.session.add(copr)
364 db.session.commit()
365
366 bitbucket_url = "https://{}/webhooks/bitbucket/{}/{}/".format(
367 app.config["PUBLIC_COPR_HOSTNAME"],
368 copr.id,
369 copr.webhook_secret)
370
371 github_url = "https://{}/webhooks/github/{}/{}/".format(
372 app.config["PUBLIC_COPR_HOSTNAME"],
373 copr.id,
374 copr.webhook_secret)
375
376 gitlab_url = "https://{}/webhooks/gitlab/{}/{}/".format(
377 app.config["PUBLIC_COPR_HOSTNAME"],
378 copr.id,
379 copr.webhook_secret)
380
381 custom_url = "https://{}/webhooks/custom/{}/{}/".format(
382 app.config["PUBLIC_COPR_HOSTNAME"],
383 copr.id,
384 copr.webhook_secret) + "<PACKAGE_NAME>/"
385
386 return flask.render_template(
387 "coprs/detail/settings/integrations.html",
388 copr=copr, bitbucket_url=bitbucket_url, github_url=github_url,
389 gitlab_url=gitlab_url, custom_url=custom_url, pagure_form=pagure_form)
390
391
392 @coprs_ns.route("/<username>/<coprname>/integrations/")
393 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/")
394 @login_required
395 @req_with_copr
396 -def copr_integrations(copr):
409
410
411 @coprs_ns.route("/<username>/<coprname>/integrations/update", methods=["POST"])
412 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/update", methods=["POST"])
413 @login_required
414 @req_with_copr
415 -def copr_integrations_update(copr):
432
444
445
446 @coprs_ns.route("/<username>/<coprname>/edit/")
447 @coprs_ns.route("/g/<group_name>/<coprname>/edit/")
448 @login_required
449 @req_with_copr
450 -def copr_edit(copr, form=None):
452
455 if "rpmfusion" in repos:
456 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>.')
457 flask.flash(message, "error")
458
492
493
494 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
495 @coprs_ns.route("/g/<group_name>/<coprname>/update/", methods=["POST"])
496 @login_required
497 @req_with_copr
498 -def copr_update(copr):
506
507
508 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/",
509 methods=["POST"])
510 @coprs_ns.route("/g/<group_name>/<coprname>/permissions_applier_change/", methods=["POST"])
511 @login_required
512 @req_with_copr
513 -def copr_permissions_applier_change(copr):
514 permission = coprs_logic.CoprPermissionsLogic.get(copr, flask.g.user).first()
515 applier_permissions_form = \
516 forms.PermissionsApplierFormFactory.create_form_cls(permission)()
517
518 if copr.user == flask.g.user:
519 flask.flash("Owner cannot request permissions for his own project.", "error")
520 elif applier_permissions_form.validate_on_submit():
521
522 if permission is not None:
523 old_builder = permission.copr_builder
524 old_admin = permission.copr_admin
525 else:
526 old_builder = 0
527 old_admin = 0
528 new_builder = applier_permissions_form.copr_builder.data
529 new_admin = applier_permissions_form.copr_admin.data
530 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier(
531 flask.g.user, copr, permission, new_builder, new_admin)
532 db.session.commit()
533 flask.flash(
534 "Successfully updated permissions for project '{0}'."
535 .format(copr.name))
536
537
538 if flask.current_app.config.get("SEND_EMAILS", False):
539 for mail in copr.admin_mails:
540 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
541 "new_builder": new_builder, "new_admin": new_admin}
542 msg = PermissionRequestMessage(copr, flask.g.user, permission_dict)
543 send_mail([mail], msg)
544
545 return flask.redirect(helpers.copr_url("coprs_ns.copr_detail", copr))
546
547
548 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
549 @coprs_ns.route("/g/<group_name>/<coprname>/update_permissions/", methods=["POST"])
550 @login_required
551 @req_with_copr
552 -def copr_update_permissions(copr):
553 permissions = copr.copr_permissions
554 permissions_form = forms.PermissionsFormFactory.create_form_cls(
555 permissions)()
556
557 if permissions_form.validate_on_submit():
558
559 try:
560
561
562 permissions.sort(
563 key=lambda x: -1 if x.user_id == flask.g.user.id else 1)
564 for perm in permissions:
565 old_builder = perm.copr_builder
566 old_admin = perm.copr_admin
567 new_builder = permissions_form[
568 "copr_builder_{0}".format(perm.user_id)].data
569 new_admin = permissions_form[
570 "copr_admin_{0}".format(perm.user_id)].data
571 coprs_logic.CoprPermissionsLogic.update_permissions(
572 flask.g.user, copr, perm, new_builder, new_admin)
573 if flask.current_app.config.get("SEND_EMAILS", False) and \
574 (old_builder is not new_builder or old_admin is not new_admin):
575 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
576 "new_builder": new_builder, "new_admin": new_admin}
577 msg = PermissionChangeMessage(copr, permission_dict)
578 send_mail(perm.user.mail, msg)
579
580
581 except exceptions.InsufficientRightsException as e:
582 db.session.rollback()
583 flask.flash(str(e), "error")
584 else:
585 db.session.commit()
586 flask.flash("Project permissions were updated successfully.", "success")
587
588 return flask.redirect(url_for_copr_details(copr))
589
590
591 @coprs_ns.route("/<username>/<coprname>/repositories/")
592 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/")
593 @login_required
594 @req_with_copr
595 -def copr_repositories(copr):
601
607
608
609 @coprs_ns.route("/<username>/<coprname>/repositories/", methods=["POST"])
610 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/", methods=["POST"])
611 @login_required
612 @req_with_copr
613 -def copr_repositories_post(copr):
614 if not flask.g.user.can_edit(copr):
615 flask.flash("You don't have access to this page.", "error")
616 return flask.redirect(url_for_copr_details(copr))
617
618 form = forms.CoprChrootExtend()
619 if form.extend.data:
620 delete_after_days = app.config["DELETE_EOL_CHROOTS_AFTER"] + 1
621 chroot_name = form.extend.data
622 flask.flash("Repository for {} will be preserved for another {} days from now"
623 .format(chroot_name, app.config["DELETE_EOL_CHROOTS_AFTER"]))
624 elif form.expire.data:
625 delete_after_days = 0
626 chroot_name = form.expire.data
627 flask.flash("Repository for {} is scheduled to be removed."
628 "If you changed your mind, click 'Extend` to revert your decision."
629 .format(chroot_name))
630 else:
631 raise ValidationError("Copr chroot needs to be either extended or expired")
632
633 copr_chroot = coprs_logic.CoprChrootsLogic.get_by_name(copr, chroot_name, active_only=False).one()
634 delete_after_timestamp = datetime.datetime.now() + datetime.timedelta(days=delete_after_days)
635 coprs_logic.CoprChrootsLogic.update_chroot(flask.g.user, copr_chroot,
636 delete_after=delete_after_timestamp)
637 db.session.commit()
638 return render_copr_repositories(copr)
639
640
641 @coprs_ns.route("/id/<copr_id>/createrepo/", methods=["POST"])
642 @login_required
643 -def copr_createrepo(copr_id):
655
658 form = forms.CoprDeleteForm()
659 if form.validate_on_submit():
660
661 try:
662 ComplexLogic.delete_copr(copr)
663 except (exceptions.ActionInProgressException,
664 exceptions.InsufficientRightsException) as e:
665
666 db.session.rollback()
667 flask.flash(str(e), "error")
668 return flask.redirect(url_on_error)
669 else:
670 db.session.commit()
671 flask.flash("Project has been deleted successfully.")
672 return flask.redirect(url_on_success)
673 else:
674 return render_template("coprs/detail/settings/delete.html", form=form, copr=copr)
675
676
677 @coprs_ns.route("/<username>/<coprname>/delete/", methods=["GET", "POST"])
678 @coprs_ns.route("/g/<group_name>/<coprname>/delete/", methods=["GET", "POST"])
679 @login_required
680 @req_with_copr
681 -def copr_delete(copr):
688
689
690 @coprs_ns.route("/<username>/<coprname>/legal_flag/", methods=["POST"])
691 @coprs_ns.route("/g/<group_name>/<coprname>/legal_flag/", methods=["POST"])
692 @login_required
693 @req_with_copr
694 -def copr_legal_flag(copr):
696
714
715
716 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
717 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/<repofile>")
718 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
719 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/<repofile>")
720 @req_with_copr_dir
721 -def generate_repo_file(copr_dir, name_release, repofile):
727
730 repo_id = "copr:{0}:{1}:{2}{3}".format(
731 app.config["PUBLIC_COPR_HOSTNAME"].split(":")[0],
732 copr_dir.copr.owner_name.replace("@", "group_"),
733 copr_dir.name,
734 ":ml" if arch else ""
735 )
736 url = os.path.join(copr_dir.repo_url, '')
737 repo_url = generate_repo_url(mock_chroot, url, arch)
738 pubkey_url = urljoin(url, "pubkey.gpg")
739 return flask.render_template("coprs/copr_dir.repo", copr_dir=copr_dir,
740 url=repo_url, pubkey_url=pubkey_url,
741 repo_id=repo_id) + "\n"
742
788
789
790
791
792
793
794 @coprs_ns.route("/<username>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
795 @coprs_ns.route("/g/<group_name>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
796 @req_with_copr
797 -def generate_module_repo_file(copr, name_release, module_nsv):
800
802 module = ModulesLogic.get_by_nsv_str(copr, module_nsv).one()
803 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
804 url = os.path.join(copr.main_dir.repo_url, '')
805 repo_url = generate_repo_url(mock_chroot, copr.modules_url)
806 baseurl = "{}+{}/latest/$basearch".format(repo_url.rstrip("/"), module_nsv)
807 pubkey_url = urljoin(url, "pubkey.gpg")
808 response = flask.make_response(
809 flask.render_template("coprs/copr-modules.cfg", copr=copr, module=module,
810 baseurl=baseurl, pubkey_url=pubkey_url))
811 response.mimetype = "text/plain"
812 response.headers["Content-Disposition"] = \
813 "filename={0}.cfg".format(copr.repo_name)
814 return response
815
816
817
818 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>")
819 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
820 try:
821 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages")
822 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm:
823 response = flask.make_response(rpm.read())
824 response.mimetype = "application/x-rpm"
825 response.headers["Content-Disposition"] = \
826 "filename={0}".format(rpmfile)
827 return response
828 except IOError:
829 return flask.render_template("404.html")
830
847
848
849 @coprs_ns.route("/<username>/<coprname>/monitor/")
850 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>")
851 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/")
852 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>")
853 @req_with_copr
854 -def copr_build_monitor(copr, detailed=False):
856
857
858 @coprs_ns.route("/<username>/<coprname>/fork/")
859 @coprs_ns.route("/g/<group_name>/<coprname>/fork/")
860 @login_required
861 @req_with_copr
862 -def copr_fork(copr):
865
868 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
869
870
871 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"])
872 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
873 @login_required
874 @req_with_copr
875 -def copr_fork_post(copr):
876 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)()
877 if form.validate_on_submit():
878 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0]
879 if flask.g.user.name != form.owner.data and not dstgroup:
880 return generic_error("There is no such group: {}".format(form.owner.data))
881
882 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup)
883 if created:
884 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes "
885 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
886 elif not created and form.confirm.data == True:
887 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes "
888 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
889 else:
890 return render_copr_fork(copr, form, confirm=True)
891
892 db.session.commit()
893 flask.flash(msg)
894
895 return flask.redirect(url_for_copr_details(fcopr))
896 return render_copr_fork(copr, form)
897
898
899 @coprs_ns.route("/<username>/<coprname>/forks/")
900 @coprs_ns.route("/g/<group_name>/<coprname>/forks/")
901 @req_with_copr
902 -def copr_forks(copr):
903 return flask.render_template("coprs/detail/forks.html", copr=copr)
904
908 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update_indexes_quick', '1'])
909 return "OK"
910
911
912 @coprs_ns.route("/<username>/<coprname>/modules/")
913 @coprs_ns.route("/g/<group_name>/<coprname>/modules/")
914 @req_with_copr
915 -def copr_modules(copr):
917
922
923
924 @coprs_ns.route("/<username>/<coprname>/create_module/")
925 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/")
926 @login_required
927 @req_with_copr
928 -def copr_create_module(copr):
931
940
941
942 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"])
943 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
944 @login_required
945 @req_with_copr
946 -def copr_create_module_post(copr):
947 form = forms.CreateModuleForm(copr=copr, meta={'csrf': False})
948 args = [copr, form]
949 if "add_profile" in flask.request.values:
950 return add_profile(*args)
951 if "build_module" in flask.request.values:
952 return build_module(*args)
953
962
965 if not form.validate_on_submit():
966
967 for i in range(2, len(form.profile_names)):
968 form.profile_pkgs.append_entry()
969 return render_create_module(copr, form, profiles=len(form.profile_names))
970
971 summary = "Module from Copr repository: {}".format(copr.full_name)
972 generator = ModulemdGenerator(str(copr.name), summary=summary, config=app.config)
973 generator.add_filter(form.filter.data)
974 generator.add_api(form.api.data)
975 generator.add_profiles(enumerate(zip(form.profile_names.data, form.profile_pkgs.data)))
976 generator.add_components(form.packages.data, form.filter.data, form.builds.data)
977 yaml = generator.generate()
978
979 facade = None
980 try:
981 facade = ModuleBuildFacade(flask.g.user, copr, yaml)
982 module = facade.submit_build()
983 db.session.commit()
984
985 flask.flash("Modulemd yaml file successfully generated and submitted to be build as {}"
986 .format(module.nsv), "success")
987 return flask.redirect(url_for_copr_details(copr))
988
989 except ValidationError as ex:
990 flask.flash(ex.message, "error")
991 return render_create_module(copr, form, len(form.profile_names))
992
993 except sqlalchemy.exc.IntegrityError:
994 flask.flash("Module {}-{}-{} already exists".format(
995 facade.modulemd.name, facade.modulemd.stream, facade.modulemd.version), "error")
996 db.session.rollback()
997 return render_create_module(copr, form, len(form.profile_names))
998
999
1000 @coprs_ns.route("/<username>/<coprname>/module/<id>")
1001 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>")
1002 @req_with_copr
1003 -def copr_module(copr, id):
1021
1022
1023 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw")
1024 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw")
1025 @req_with_copr
1026 -def copr_module_raw(copr, id):
1033