1
2
3 import os
4 import time
5 import fnmatch
6 import uuid
7 import subprocess
8 from six.moves.urllib.parse import urljoin
9
10 import flask
11 from flask import render_template, url_for, stream_with_context
12 import platform
13 import smtplib
14 import tempfile
15 import sqlalchemy
16 import modulemd
17 from email.mime.text import MIMEText
18 from itertools import groupby
19 from wtforms import ValidationError
20
21 from pygments import highlight
22 from pygments.lexers import get_lexer_by_name
23 from pygments.formatters import HtmlFormatter
24
25 from coprs import app
26 from coprs import db
27 from coprs import rcp
28 from coprs import exceptions
29 from coprs import forms
30 from coprs import helpers
31 from coprs import models
32 from coprs.exceptions import ObjectNotFound
33 from coprs.logic.coprs_logic import CoprsLogic
34 from coprs.logic.packages_logic import PackagesLogic
35 from coprs.logic.stat_logic import CounterStatLogic
36 from coprs.logic.users_logic import UsersLogic
37 from coprs.logic.modules_logic import ModulesLogic, ModulemdGenerator, ModuleBuildFacade
38 from coprs.rmodels import TimedStatEvents
39
40 from coprs.logic.complex_logic import ComplexLogic
41
42 from coprs.views.misc import login_required, page_not_found, req_with_copr, req_with_copr, generic_error
43
44 from coprs.views.coprs_ns import coprs_ns
45 from coprs.views.groups_ns import groups_ns
46
47 from coprs.logic import builds_logic, coprs_logic, actions_logic, users_logic
48 from coprs.helpers import parse_package_name, generate_repo_url, CHROOT_RPMS_DL_STAT_FMT, CHROOT_REPO_MD_DL_STAT_FMT, \
49 str2bool, url_for_copr_view, REPO_DL_STAT_FMT, CounterStatType
56
63
64
65 @coprs_ns.route("/", defaults={"page": 1})
66 @coprs_ns.route("/<int:page>/")
67 -def coprs_show(page=1):
88
89
90 @coprs_ns.route("/<username>/", defaults={"page": 1})
91 @coprs_ns.route("/<username>/<int:page>/")
92 -def coprs_by_user(username=None, page=1):
93 user = users_logic.UsersLogic.get(username).first()
94 if not user:
95 return page_not_found(
96 "User {0} does not exist.".format(username))
97
98 query = CoprsLogic.get_multiple_owned_by_username(username)
99 query = CoprsLogic.filter_without_group_projects(query)
100 query = CoprsLogic.set_query_order(query, desc=True)
101
102 paginator = helpers.Paginator(query, query.count(), page)
103
104 coprs = paginator.sliced_query
105
106
107 users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 4)
108
109 data = builds_logic.BuildsLogic.get_running_tasks_from_last_day()
110
111 return flask.render_template("coprs/show/user.html",
112 user=user,
113 coprs=coprs,
114 paginator=paginator,
115 tasks_info=ComplexLogic.get_queue_sizes(),
116 users_builds=users_builds,
117 graph=data)
118
119
120 @coprs_ns.route("/fulltext/", defaults={"page": 1})
121 @coprs_ns.route("/fulltext/<int:page>/")
122 -def coprs_fulltext_search(page=1):
123 fulltext = flask.request.args.get("fulltext", "")
124 try:
125 query = coprs_logic.CoprsLogic.get_multiple_fulltext(fulltext)
126 except ValueError as e:
127 flask.flash(str(e), "error")
128 return flask.redirect(flask.request.referrer or
129 flask.url_for("coprs_ns.coprs_show"))
130
131 paginator = helpers.Paginator(query, query.count(), page,
132 additional_params={"fulltext": fulltext})
133
134 data = builds_logic.BuildsLogic.get_running_tasks_from_last_day()
135
136 coprs = paginator.sliced_query
137 return render_template("coprs/show/fulltext.html",
138 coprs=coprs,
139 paginator=paginator,
140 fulltext=fulltext,
141 tasks_info=ComplexLogic.get_queue_sizes(),
142 graph=data)
143
144
145 @coprs_ns.route("/<username>/add/")
146 @coprs_ns.route("/g/<group_name>/add/")
147 @login_required
148 -def copr_add(username=None, group_name=None):
154
155
156 @coprs_ns.route("/<username>/new/", methods=["POST"])
157 @coprs_ns.route("/g/<group_name>/new/", methods=["POST"])
158 @login_required
159 -def copr_new(username=None, group_name=None):
163
166 group = ComplexLogic.get_group_by_name_safe(group_name)
167 form = forms.CoprFormFactory.create_form_cls(group=group)()
168
169 if form.validate_on_submit():
170 try:
171 copr = coprs_logic.CoprsLogic.add(
172 flask.g.user,
173 name=form.name.data,
174 homepage=form.homepage.data,
175 contact=form.contact.data,
176 repos=form.repos.data.replace("\n", " "),
177 selected_chroots=form.selected_chroots,
178 description=form.description.data,
179 instructions=form.instructions.data,
180 disable_createrepo=form.disable_createrepo.data,
181 build_enable_net=form.build_enable_net.data,
182 unlisted_on_hp=form.unlisted_on_hp.data,
183 group=group,
184 persistent=form.persistent.data,
185 auto_prune=(form.auto_prune.data if flask.g.user.admin else True),
186 use_bootstrap_container=form.use_bootstrap_container.data,
187 follow_fedora_branching=form.follow_fedora_branching.data,
188 )
189 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e:
190 flask.flash(str(e), "error")
191 return flask.render_template("coprs/group_add.html", form=form, group=group)
192
193 db.session.add(copr)
194 db.session.commit()
195 after_the_project_creation(copr, form)
196
197 return flask.redirect(url_for_copr_details(copr))
198 else:
199 return flask.render_template("coprs/group_add.html", form=form, group=group)
200
203 """
204 Receive information from the user on how to create its new copr
205 and create it accordingly.
206 """
207
208 form = forms.CoprFormFactory.create_form_cls()()
209 if form.validate_on_submit():
210 try:
211 copr = coprs_logic.CoprsLogic.add(
212 flask.g.user,
213 name=form.name.data,
214 homepage=form.homepage.data,
215 contact=form.contact.data,
216 repos=form.repos.data.replace("\n", " "),
217 selected_chroots=form.selected_chroots,
218 description=form.description.data,
219 instructions=form.instructions.data,
220 disable_createrepo=form.disable_createrepo.data,
221 build_enable_net=form.build_enable_net.data,
222 unlisted_on_hp=form.unlisted_on_hp.data,
223 persistent=form.persistent.data,
224 auto_prune=(form.auto_prune.data if flask.g.user.admin else True),
225 use_bootstrap_container=form.use_bootstrap_container.data,
226 follow_fedora_branching=form.follow_fedora_branching.data,
227 )
228 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e:
229 flask.flash(str(e), "error")
230 return flask.render_template("coprs/add.html", form=form)
231
232 db.session.commit()
233 after_the_project_creation(copr, form)
234
235 return flask.redirect(url_for_copr_details(copr))
236 else:
237 return flask.render_template("coprs/add.html", form=form)
238
241 flask.flash("New project has been created successfully.", "success")
242 _check_rpmfusion(copr.repos)
243 if form.initial_pkgs.data:
244 pkgs = form.initial_pkgs.data.replace("\n", " ").split(" ")
245
246
247 bad_urls = []
248 for pkg in pkgs:
249 if not pkg.endswith(".src.rpm"):
250 bad_urls.append(pkg)
251 flask.flash("Bad url: {0} (skipped)".format(pkg))
252 for bad_url in bad_urls:
253 pkgs.remove(bad_url)
254
255 if not pkgs:
256 flask.flash("No initial packages submitted")
257 else:
258
259 for pkg in pkgs:
260 builds_logic.BuildsLogic.add(
261 flask.g.user,
262 pkgs=pkg,
263 srpm_url=pkg,
264 copr=copr,
265 enable_net=form.build_enable_net.data
266 )
267
268 db.session.commit()
269 flask.flash("Initial packages were successfully submitted "
270 "for building.")
271
272
273 @coprs_ns.route("/<username>/<coprname>/report-abuse")
274 @coprs_ns.route("/g/<group_name>/<coprname>/report-abuse")
275 @req_with_copr
276 @login_required
277 -def copr_report_abuse(copr):
279
284
285
286 @coprs_ns.route("/<username>/<coprname>/")
287 @coprs_ns.route("/g/<group_name>/<coprname>/")
288 @req_with_copr
289 -def copr_detail(copr):
291
294 repo_dl_stat = CounterStatLogic.get_copr_repo_dl_stat(copr)
295 form = forms.CoprLegalFlagForm()
296 repos_info = {}
297 for chroot in copr.active_chroots:
298
299
300
301
302
303 chroot_rpms_dl_stat_key = CHROOT_RPMS_DL_STAT_FMT.format(
304 copr_user=copr.user.name,
305 copr_project_name=copr.name,
306 copr_chroot=chroot.name,
307 )
308 chroot_rpms_dl_stat = TimedStatEvents.get_count(
309 rconnect=rcp.get_connection(),
310 name=chroot_rpms_dl_stat_key,
311 )
312
313 logoset = set()
314 logodir = app.static_folder + "/chroot_logodir"
315 for logo in os.listdir(logodir):
316
317 if fnmatch.fnmatch(logo, "*.png"):
318 logoset.add(logo.strip(".png"))
319
320 if chroot.name_release not in repos_info:
321 logo = None
322 if chroot.name_release in logoset:
323 logo = chroot.name_release + ".png"
324 elif chroot.os_release in logoset:
325 logo = chroot.os_release + ".png"
326
327 repos_info[chroot.name_release] = {
328 "name_release": chroot.name_release,
329 "name_release_human": chroot.name_release_human,
330 "os_release": chroot.os_release,
331 "os_version": chroot.os_version,
332 "logo": logo,
333 "arch_list": [chroot.arch],
334 "repo_file": "{}-{}.repo".format(copr.repo_id, chroot.name_release),
335 "dl_stat": repo_dl_stat[chroot.name_release],
336 "rpm_dl_stat": {
337 chroot.arch: chroot_rpms_dl_stat
338 }
339 }
340 else:
341 repos_info[chroot.name_release]["arch_list"].append(chroot.arch)
342 repos_info[chroot.name_release]["rpm_dl_stat"][chroot.arch] = chroot_rpms_dl_stat
343 repos_info_list = sorted(repos_info.values(), key=lambda rec: rec["name_release"])
344 builds = builds_logic.BuildsLogic.get_multiple_by_copr(copr=copr).limit(1).all()
345
346 return flask.render_template(
347 "coprs/detail/overview.html",
348 copr=copr,
349 user=flask.g.user,
350 form=form,
351 repo_dl_stat=repo_dl_stat,
352 repos_info_list=repos_info_list,
353 latest_build=builds[0] if len(builds) == 1 else None,
354 )
355
356
357 @coprs_ns.route("/<username>/<coprname>/permissions/")
358 @req_with_copr
359 -def copr_permissions(copr):
387
390 if not copr.webhook_secret:
391 copr.webhook_secret = str(uuid.uuid4())
392 db.session.add(copr)
393 db.session.commit()
394
395 bitbucket_url = "https://{}/webhooks/bitbucket/{}/{}/".format(
396 app.config["PUBLIC_COPR_HOSTNAME"],
397 copr.id,
398 copr.webhook_secret)
399
400 github_url = "https://{}/webhooks/github/{}/{}/".format(
401 app.config["PUBLIC_COPR_HOSTNAME"],
402 copr.id,
403 copr.webhook_secret)
404
405 gitlab_url = "https://{}/webhooks/gitlab/{}/{}/".format(
406 app.config["PUBLIC_COPR_HOSTNAME"],
407 copr.id,
408 copr.webhook_secret)
409
410 custom_url = "https://{}/webhooks/custom/{}/{}/".format(
411 app.config["PUBLIC_COPR_HOSTNAME"],
412 copr.id,
413 copr.webhook_secret) + "<PACKAGE_NAME>/"
414
415 return flask.render_template(
416 "coprs/detail/settings/webhooks.html",
417 copr=copr, bitbucket_url=bitbucket_url, github_url=github_url,
418 gitlab_url=gitlab_url, custom_url=custom_url)
419
420
421 @coprs_ns.route("/<username>/<coprname>/webhooks/")
422 @coprs_ns.route("/g/<group_name>/<coprname>/webhooks/")
423 @login_required
424 @req_with_copr
425 -def copr_webhooks(copr):
427
436
437
438 @coprs_ns.route("/<username>/<coprname>/edit/")
439 @coprs_ns.route("/g/<group_name>/<coprname>/edit/")
440 @login_required
441 @req_with_copr
442 -def copr_edit(copr, form=None):
444
447 if "rpmfusion" in repos:
448 message = flask.Markup('Using rpmfusion as dependency is nearly always wrong. Please see <a href="https://docs.pagure.org/copr.copr/user_documentation.html#what-i-can-build-in-copr">What I can build in Copr</a>.')
449 flask.flash(message, "error")
450
482
483
484 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
485 @coprs_ns.route("/g/<group_name>/<coprname>/update/", methods=["POST"])
486 @login_required
487 @req_with_copr
488 -def copr_update(copr):
496
497
498 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/",
499 methods=["POST"])
503 permission = coprs_logic.CoprPermissionsLogic.get(copr, flask.g.user).first()
504 applier_permissions_form = \
505 forms.PermissionsApplierFormFactory.create_form_cls(permission)()
506
507 if copr.user == flask.g.user:
508 flask.flash("Owner cannot request permissions for his own project.", "error")
509 elif applier_permissions_form.validate_on_submit():
510
511 if permission is not None:
512 old_builder = permission.copr_builder
513 old_admin = permission.copr_admin
514 else:
515 old_builder = 0
516 old_admin = 0
517 new_builder = applier_permissions_form.copr_builder.data
518 new_admin = applier_permissions_form.copr_admin.data
519 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier(
520 flask.g.user, copr, permission, new_builder, new_admin)
521 db.session.commit()
522 flask.flash(
523 "Successfully updated permissions for project '{0}'."
524 .format(copr.name))
525 admin_mails = [copr.user.mail]
526 for perm in copr.copr_permissions:
527
528 if perm.copr_admin == 2:
529 admin_mails.append(perm.user.mail)
530
531
532 if flask.current_app.config.get("SEND_EMAILS", False):
533 for mail in admin_mails:
534 msg = MIMEText(
535 "{6} is asking for these permissions:\n\n"
536 "Builder: {0} -> {1}\nAdmin: {2} -> {3}\n\n"
537 "Project: {4}\nOwner: {5}".format(
538 helpers.PermissionEnum(old_builder),
539 helpers.PermissionEnum(new_builder),
540 helpers.PermissionEnum(old_admin),
541 helpers.PermissionEnum(new_admin),
542 copr.name, copr.user.name, flask.g.user.name))
543
544 msg["Subject"] = "[Copr] {0}: {1} is asking permissions".format(copr.name, flask.g.user.name)
545 msg["From"] = "root@{0}".format(platform.node())
546 msg["To"] = mail
547 s = smtplib.SMTP("localhost")
548 s.sendmail("root@{0}".format(platform.node()), mail, msg.as_string())
549 s.quit()
550
551 return flask.redirect(flask.url_for("coprs_ns.copr_detail",
552 username=copr.user.name,
553 coprname=copr.name))
554
555
556 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
557 @login_required
558 @req_with_copr
559 -def copr_update_permissions(copr):
560 permissions = copr.copr_permissions
561 permissions_form = forms.PermissionsFormFactory.create_form_cls(
562 permissions)()
563
564 if permissions_form.validate_on_submit():
565
566 try:
567
568
569 permissions.sort(
570 key=lambda x: -1 if x.user_id == flask.g.user.id else 1)
571 for perm in permissions:
572 old_builder = perm.copr_builder
573 old_admin = perm.copr_admin
574 new_builder = permissions_form[
575 "copr_builder_{0}".format(perm.user_id)].data
576 new_admin = permissions_form[
577 "copr_admin_{0}".format(perm.user_id)].data
578 coprs_logic.CoprPermissionsLogic.update_permissions(
579 flask.g.user, copr, perm, new_builder, new_admin)
580 if flask.current_app.config.get("SEND_EMAILS", False) and \
581 (old_builder is not new_builder or old_admin is not new_admin):
582
583 msg = MIMEText(
584 "Your permissions have changed:\n\n"
585 "Builder: {0} -> {1}\nAdmin: {2} -> {3}\n\n"
586 "Project: {4}\nOwner: {5}".format(
587 helpers.PermissionEnum(old_builder),
588 helpers.PermissionEnum(new_builder),
589 helpers.PermissionEnum(old_admin),
590 helpers.PermissionEnum(new_admin),
591 copr.name, copr.user.name))
592
593 msg["Subject"] = "[Copr] {0}: Your permissions have changed".format(copr.name)
594 msg["From"] = "root@{0}".format(platform.node())
595 msg["To"] = perm.user.mail
596 s = smtplib.SMTP("localhost")
597 s.sendmail("root@{0}".format(platform.node()), perm.user.mail, msg.as_string())
598 s.quit()
599
600
601 except exceptions.InsufficientRightsException as e:
602 db.session.rollback()
603 flask.flash(str(e), "error")
604 else:
605 db.session.commit()
606 flask.flash("Project permissions were updated successfully.", "success")
607
608 return flask.redirect(url_for_copr_details(copr))
609
610
611 @coprs_ns.route("/id/<copr_id>/createrepo/", methods=["POST"])
612 @login_required
613 -def copr_createrepo(copr_id):
628
631 form = forms.CoprDeleteForm()
632 if form.validate_on_submit():
633
634 try:
635 ComplexLogic.delete_copr(copr)
636 except (exceptions.ActionInProgressException,
637 exceptions.InsufficientRightsException) as e:
638
639 db.session.rollback()
640 flask.flash(str(e), "error")
641 return flask.redirect(url_on_error)
642 else:
643 db.session.commit()
644 flask.flash("Project has been deleted successfully.")
645 return flask.redirect(url_on_success)
646 else:
647 return render_template("coprs/detail/settings/delete.html", form=form, copr=copr)
648
649
650 @coprs_ns.route("/<username>/<coprname>/delete/", methods=["GET", "POST"])
651 @coprs_ns.route("/g/<group_name>/<coprname>/delete/", methods=["GET", "POST"])
652 @login_required
653 @req_with_copr
654 -def copr_delete(copr):
661
662
663 @coprs_ns.route("/<username>/<coprname>/legal_flag/", methods=["POST"])
664 @coprs_ns.route("/g/<group_name>/<coprname>/legal_flag/", methods=["POST"])
665 @login_required
666 @req_with_copr
667 -def copr_legal_flag(copr):
670
673 form = forms.CoprLegalFlagForm()
674 legal_flag = models.LegalFlag(raise_message=form.comment.data,
675 raised_on=int(time.time()),
676 copr=copr,
677 reporter=flask.g.user)
678 db.session.add(legal_flag)
679 db.session.commit()
680 send_to = app.config["SEND_LEGAL_TO"] or ["root@localhost"]
681 hostname = platform.node()
682 navigate_to = "\nNavigate to http://{0}{1}".format(
683 hostname, flask.url_for("admin_ns.legal_flag"))
684 contact = "\nContact on owner is: {}".format(contact_info)
685 reported_by = "\nReported by {0} <{1}>".format(flask.g.user.name,
686 flask.g.user.mail)
687 try:
688 msg = MIMEText(
689 form.comment.data + navigate_to + contact + reported_by, "plain")
690 except UnicodeEncodeError:
691 msg = MIMEText(form.comment.data.encode(
692 "utf-8") + navigate_to + contact + reported_by, "plain", "utf-8")
693 msg["Subject"] = "Legal flag raised on {0}".format(copr.name)
694 msg["From"] = "root@{0}".format(hostname)
695 msg["To"] = ", ".join(send_to)
696 s = smtplib.SMTP("localhost")
697 s.sendmail("root@{0}".format(hostname), send_to, msg.as_string())
698 s.quit()
699 flask.flash("Admin has been noticed about your report"
700 " and will investigate the project shortly.")
701 return flask.redirect(url_for_copr_details(copr))
702
703
704 @coprs_ns.route("/<username>/<coprname>/repo/<name_release>/", defaults={"repofile": None})
705 @coprs_ns.route("/<username>/<coprname>/repo/<name_release>/<repofile>")
706 @coprs_ns.route("/g/<group_name>/<coprname>/repo/<name_release>/", defaults={"repofile": None})
707 @coprs_ns.route("/g/<group_name>/<coprname>/repo/<name_release>/<repofile>")
708 -def generate_repo_file(coprname, name_release, repofile, username=None, group_name=None):
726
729
730
731 if name_release in [c.name for c in copr.mock_chroots]:
732 chroot = [c for c in copr.mock_chroots if c.name == name_release][0]
733 kwargs = dict(coprname=copr.name, name_release=chroot.name_release)
734 fixed_url = helpers.copr_url("coprs_ns.generate_repo_file", copr, **kwargs)
735 return flask.redirect(fixed_url)
736
737 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
738 if not mock_chroot:
739 raise ObjectNotFound("Chroot {} does not exist".format(name_release))
740
741 url = os.path.join(copr.repo_url, '')
742 repo_url = generate_repo_url(mock_chroot, url)
743 pubkey_url = urljoin(url, "pubkey.gpg")
744 response = flask.make_response(
745 flask.render_template("coprs/copr.repo", copr=copr, url=repo_url, pubkey_url=pubkey_url))
746 response.mimetype = "text/plain"
747 response.headers["Content-Disposition"] = \
748 "filename={0}.repo".format(copr.repo_name)
749
750 name = REPO_DL_STAT_FMT.format(**{
751 'copr_user': copr.user.name,
752 'copr_project_name': copr.name,
753 'copr_name_release': name_release,
754 })
755 CounterStatLogic.incr(name=name, counter_type=CounterStatType.REPO_DL)
756 db.session.commit()
757
758 return response
759
760
761
762
763
764
765 @coprs_ns.route("/<username>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
766 @coprs_ns.route("/g/<group_name>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
767 @req_with_copr
768 -def generate_module_repo_file(copr, name_release, module_nsv):
771
773 module = ModulesLogic.get_by_nsv_str(copr, module_nsv).one()
774 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
775 url = os.path.join(copr.repo_url, '')
776 repo_url = generate_repo_url(mock_chroot, copr.modules_url)
777 baseurl = "{}+{}/latest/$basearch".format(repo_url.rstrip("/"), module_nsv)
778 pubkey_url = urljoin(url, "pubkey.gpg")
779 response = flask.make_response(
780 flask.render_template("coprs/copr-modules.cfg", copr=copr, module=module,
781 baseurl=baseurl, pubkey_url=pubkey_url))
782 response.mimetype = "text/plain"
783 response.headers["Content-Disposition"] = \
784 "filename={0}.cfg".format(copr.repo_name)
785 return response
786
787
788
789 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>")
790 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
791 try:
792 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages")
793 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm:
794 response = flask.make_response(rpm.read())
795 response.mimetype = "application/x-rpm"
796 response.headers["Content-Disposition"] = \
797 "filename={0}".format(rpmfile)
798 return response
799 except IOError:
800 return flask.render_template("404.html")
801
818
819
820 @coprs_ns.route("/<username>/<coprname>/monitor/")
821 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>")
822 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/")
823 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>")
824 @req_with_copr
825 -def copr_build_monitor(copr, detailed=False):
827
828
829 @coprs_ns.route("/<username>/<coprname>/fork/")
830 @coprs_ns.route("/g/<group_name>/<coprname>/fork/")
831 @login_required
832 @req_with_copr
833 -def copr_fork(copr):
836
839 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
840
841
842 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"])
843 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
844 @login_required
845 @req_with_copr
846 -def copr_fork_post(copr):
847 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)()
848 if form.validate_on_submit():
849 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0]
850 if flask.g.user.name != form.owner.data and not dstgroup:
851 return generic_error("There is no such group: {}".format(form.owner.data))
852
853 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup)
854 if created:
855 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes "
856 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
857 elif not created and form.confirm.data == True:
858 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes "
859 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
860 else:
861 return render_copr_fork(copr, form, confirm=True)
862
863 db.session.commit()
864 flask.flash(msg)
865
866 return flask.redirect(url_for_copr_details(fcopr))
867 return render_copr_fork(copr, form)
868
872 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update_indexes_quick', '1'])
873 return "OK"
874
875
876 @coprs_ns.route("/<username>/<coprname>/modules/")
877 @coprs_ns.route("/g/<group_name>/<coprname>/modules/")
878 @req_with_copr
879 -def copr_modules(copr):
881
886
887
888 @coprs_ns.route("/<username>/<coprname>/create_module/")
889 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/")
890 @login_required
891 @req_with_copr
892 -def copr_create_module(copr):
895
904
905
906 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"])
907 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
908 @login_required
909 @req_with_copr
910 -def copr_create_module_post(copr):
911 form = forms.CreateModuleForm(copr=copr, csrf_enabled=False)
912 args = [copr, form]
913 if "add_profile" in flask.request.values:
914 return add_profile(*args)
915 if "build_module" in flask.request.values:
916 return build_module(*args)
917
926
929 if not form.validate_on_submit():
930
931 for i in range(2, len(form.profile_names)):
932 form.profile_pkgs.append_entry()
933 return render_create_module(copr, form, profiles=len(form.profile_names))
934
935 summary = "Module from Copr repository: {}".format(copr.full_name)
936 generator = ModulemdGenerator(str(copr.name), summary=summary, config=app.config)
937 generator.add_filter(form.filter.data)
938 generator.add_api(form.api.data)
939 generator.add_profiles(enumerate(zip(form.profile_names.data, form.profile_pkgs.data)))
940 generator.add_components(form.packages.data, form.filter.data, form.builds.data)
941 yaml = generator.generate()
942
943 facade = None
944 try:
945 facade = ModuleBuildFacade(flask.g.user, copr, yaml)
946 module = facade.submit_build()
947 db.session.commit()
948
949 flask.flash("Modulemd yaml file successfully generated and submitted to be build as {}"
950 .format(module.nsv), "success")
951 return flask.redirect(url_for_copr_details(copr))
952
953 except ValidationError as ex:
954 flask.flash(ex.message, "error")
955 return render_create_module(copr, form, len(form.profile_names))
956
957 except sqlalchemy.exc.IntegrityError:
958 flask.flash("Module {}-{}-{} already exists".format(
959 facade.modulemd.name, facade.modulemd.stream, facade.modulemd.version), "error")
960 db.session.rollback()
961 return render_create_module(copr, form, len(form.profile_names))
962
963
964 @coprs_ns.route("/<username>/<coprname>/module/<id>")
965 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>")
966 @req_with_copr
967 -def copr_module(copr, id):
985
986
987 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw")
988 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw")
989 @req_with_copr
990 -def copr_module_raw(copr, id):
997