1
2
3 import os
4 import time
5 import fnmatch
6 import uuid
7 import subprocess
8 import json
9
10 from six.moves.urllib.parse import urljoin
11
12 import flask
13 from flask import render_template, url_for, stream_with_context
14 import platform
15 import smtplib
16 import tempfile
17 import sqlalchemy
18 import modulemd
19 from email.mime.text import MIMEText
20 from itertools import groupby
21 from wtforms import ValidationError
22
23 from pygments import highlight
24 from pygments.lexers import get_lexer_by_name
25 from pygments.formatters import HtmlFormatter
26
27 from coprs import app
28 from coprs import db
29 from coprs import rcp
30 from coprs import exceptions
31 from coprs import forms
32 from coprs import helpers
33 from coprs import models
34 from coprs.exceptions import ObjectNotFound
35 from coprs.logic.coprs_logic import CoprsLogic
36 from coprs.logic.packages_logic import PackagesLogic
37 from coprs.logic.stat_logic import CounterStatLogic
38 from coprs.logic.modules_logic import ModulesLogic, ModulemdGenerator, ModuleBuildFacade
39 from coprs.rmodels import TimedStatEvents
40
41 from coprs.logic.complex_logic import ComplexLogic
42
43 from coprs.views.misc import login_required, page_not_found, req_with_copr, req_with_copr, generic_error
44
45 from coprs.views.coprs_ns import coprs_ns
46 from coprs.views.groups_ns import groups_ns
47
48 from coprs.logic import builds_logic, coprs_logic, actions_logic, users_logic
49 from coprs.helpers import parse_package_name, generate_repo_url, CHROOT_RPMS_DL_STAT_FMT, CHROOT_REPO_MD_DL_STAT_FMT, \
50 str2bool, url_for_copr_view, REPO_DL_STAT_FMT, CounterStatType
57
64
65
66 @coprs_ns.route("/", defaults={"page": 1})
67 @coprs_ns.route("/<int:page>/")
68 -def coprs_show(page=1):
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 query = CoprsLogic.get_multiple_owned_by_username(username)
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
105 coprs = paginator.sliced_query
106
107
108 users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 4)
109
110 data = builds_logic.BuildsLogic.get_running_tasks_from_last_day()
111
112 return flask.render_template("coprs/show/user.html",
113 user=user,
114 coprs=coprs,
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_running_tasks_from_last_day()
136
137 coprs = paginator.sliced_query
138 return render_template("coprs/show/fulltext.html",
139 coprs=coprs,
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):
164
167 group = ComplexLogic.get_group_by_name_safe(group_name)
168 form = forms.CoprFormFactory.create_form_cls(group=group)()
169
170 if form.validate_on_submit():
171 try:
172 copr = coprs_logic.CoprsLogic.add(
173 flask.g.user,
174 name=form.name.data,
175 homepage=form.homepage.data,
176 contact=form.contact.data,
177 repos=form.repos.data.replace("\n", " "),
178 selected_chroots=form.selected_chroots,
179 description=form.description.data,
180 instructions=form.instructions.data,
181 disable_createrepo=form.disable_createrepo.data,
182 build_enable_net=form.build_enable_net.data,
183 unlisted_on_hp=form.unlisted_on_hp.data,
184 group=group,
185 persistent=form.persistent.data,
186 auto_prune=(form.auto_prune.data if flask.g.user.admin else True),
187 use_bootstrap_container=form.use_bootstrap_container.data,
188 follow_fedora_branching=form.follow_fedora_branching.data,
189 )
190 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e:
191 flask.flash(str(e), "error")
192 return flask.render_template("coprs/group_add.html", form=form, group=group)
193
194 db.session.add(copr)
195 db.session.commit()
196 after_the_project_creation(copr, form)
197
198 return flask.redirect(url_for_copr_details(copr))
199 else:
200 return flask.render_template("coprs/group_add.html", form=form, group=group)
201
204 """
205 Receive information from the user on how to create its new copr
206 and create it accordingly.
207 """
208
209 form = forms.CoprFormFactory.create_form_cls()()
210 if form.validate_on_submit():
211 try:
212 copr = coprs_logic.CoprsLogic.add(
213 flask.g.user,
214 name=form.name.data,
215 homepage=form.homepage.data,
216 contact=form.contact.data,
217 repos=form.repos.data.replace("\n", " "),
218 selected_chroots=form.selected_chroots,
219 description=form.description.data,
220 instructions=form.instructions.data,
221 disable_createrepo=form.disable_createrepo.data,
222 build_enable_net=form.build_enable_net.data,
223 unlisted_on_hp=form.unlisted_on_hp.data,
224 persistent=form.persistent.data,
225 auto_prune=(form.auto_prune.data if flask.g.user.admin else True),
226 use_bootstrap_container=form.use_bootstrap_container.data,
227 follow_fedora_branching=form.follow_fedora_branching.data,
228 )
229 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e:
230 flask.flash(str(e), "error")
231 return flask.render_template("coprs/add.html", form=form)
232
233 db.session.commit()
234 after_the_project_creation(copr, form)
235
236 return flask.redirect(url_for_copr_details(copr))
237 else:
238 return flask.render_template("coprs/add.html", form=form)
239
242 flask.flash("New project has been created successfully.", "success")
243 _check_rpmfusion(copr.repos)
244 if form.initial_pkgs.data:
245 pkgs = form.initial_pkgs.data.replace("\n", " ").split(" ")
246
247
248 bad_urls = []
249 for pkg in pkgs:
250 if not pkg.endswith(".src.rpm"):
251 bad_urls.append(pkg)
252 flask.flash("Bad url: {0} (skipped)".format(pkg))
253 for bad_url in bad_urls:
254 pkgs.remove(bad_url)
255
256 if not pkgs:
257 flask.flash("No initial packages submitted")
258 else:
259
260 for pkg in pkgs:
261 builds_logic.BuildsLogic.add(
262 flask.g.user,
263 pkgs=pkg,
264 srpm_url=pkg,
265 copr=copr,
266 enable_net=form.build_enable_net.data
267 )
268
269 db.session.commit()
270 flask.flash("Initial packages were successfully submitted "
271 "for building.")
272
273
274 @coprs_ns.route("/<username>/<coprname>/report-abuse")
275 @coprs_ns.route("/g/<group_name>/<coprname>/report-abuse")
276 @req_with_copr
277 @login_required
278 -def copr_report_abuse(copr):
280
285
286
287 @coprs_ns.route("/<username>/<coprname>/")
288 @coprs_ns.route("/g/<group_name>/<coprname>/")
289 @req_with_copr
290 -def copr_detail(copr):
292
295 repo_dl_stat = CounterStatLogic.get_copr_repo_dl_stat(copr)
296 form = forms.CoprLegalFlagForm()
297 repos_info = {}
298 for chroot in copr.active_chroots:
299
300
301
302
303
304 chroot_rpms_dl_stat_key = CHROOT_RPMS_DL_STAT_FMT.format(
305 copr_user=copr.owner_name,
306 copr_project_name=copr.name,
307 copr_chroot=chroot.name,
308 )
309 chroot_rpms_dl_stat = TimedStatEvents.get_count(
310 rconnect=rcp.get_connection(),
311 name=chroot_rpms_dl_stat_key,
312 )
313
314 logoset = set()
315 logodir = app.static_folder + "/chroot_logodir"
316 for logo in os.listdir(logodir):
317
318 if fnmatch.fnmatch(logo, "*.png"):
319 logoset.add(logo[:-4])
320
321 if chroot.name_release not in repos_info:
322 logo = None
323 if chroot.name_release in logoset:
324 logo = chroot.name_release + ".png"
325 elif chroot.os_release in logoset:
326 logo = chroot.os_release + ".png"
327
328 repos_info[chroot.name_release] = {
329 "name_release": chroot.name_release,
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.new_webhook_secret()
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/integrations.html",
417 copr=copr, bitbucket_url=bitbucket_url, github_url=github_url,
418 gitlab_url=gitlab_url, custom_url=custom_url, pagure_form=pagure_form)
419
420
421 @coprs_ns.route("/<username>/<coprname>/integrations/")
422 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/")
423 @login_required
424 @req_with_copr
425 -def copr_integrations(copr):
438
439
440 @coprs_ns.route("/<username>/<coprname>/integrations/update", methods=["POST"])
441 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/update", methods=["POST"])
442 @login_required
443 @req_with_copr
444 -def copr_integrations_update(copr):
461
470
471
472 @coprs_ns.route("/<username>/<coprname>/edit/")
473 @coprs_ns.route("/g/<group_name>/<coprname>/edit/")
474 @login_required
475 @req_with_copr
476 -def copr_edit(copr, form=None):
478
481 if "rpmfusion" in repos:
482 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>.')
483 flask.flash(message, "error")
484
516
517
518 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
519 @coprs_ns.route("/g/<group_name>/<coprname>/update/", methods=["POST"])
520 @login_required
521 @req_with_copr
522 -def copr_update(copr):
530
531
532 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/",
533 methods=["POST"])
537 permission = coprs_logic.CoprPermissionsLogic.get(copr, flask.g.user).first()
538 applier_permissions_form = \
539 forms.PermissionsApplierFormFactory.create_form_cls(permission)()
540
541 if copr.user == flask.g.user:
542 flask.flash("Owner cannot request permissions for his own project.", "error")
543 elif applier_permissions_form.validate_on_submit():
544
545 if permission is not None:
546 old_builder = permission.copr_builder
547 old_admin = permission.copr_admin
548 else:
549 old_builder = 0
550 old_admin = 0
551 new_builder = applier_permissions_form.copr_builder.data
552 new_admin = applier_permissions_form.copr_admin.data
553 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier(
554 flask.g.user, copr, permission, new_builder, new_admin)
555 db.session.commit()
556 flask.flash(
557 "Successfully updated permissions for project '{0}'."
558 .format(copr.name))
559 admin_mails = [copr.user.mail]
560 for perm in copr.copr_permissions:
561
562 if perm.copr_admin == 2:
563 admin_mails.append(perm.user.mail)
564
565
566 if flask.current_app.config.get("SEND_EMAILS", False):
567 for mail in admin_mails:
568 msg = MIMEText(
569 "{6} is asking for these permissions:\n\n"
570 "Builder: {0} -> {1}\nAdmin: {2} -> {3}\n\n"
571 "Project: {4}\nOwner: {5}".format(
572 helpers.PermissionEnum(old_builder),
573 helpers.PermissionEnum(new_builder),
574 helpers.PermissionEnum(old_admin),
575 helpers.PermissionEnum(new_admin),
576 copr.name, copr.user.name, flask.g.user.name))
577
578 msg["Subject"] = "[Copr] {0}: {1} is asking permissions".format(copr.name, flask.g.user.name)
579 msg["From"] = "root@{0}".format(platform.node())
580 msg["To"] = mail
581 s = smtplib.SMTP("localhost")
582 s.sendmail("root@{0}".format(platform.node()), mail, msg.as_string())
583 s.quit()
584
585 return flask.redirect(flask.url_for("coprs_ns.copr_detail",
586 username=copr.user.name,
587 coprname=copr.name))
588
589
590 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
591 @login_required
592 @req_with_copr
593 -def copr_update_permissions(copr):
594 permissions = copr.copr_permissions
595 permissions_form = forms.PermissionsFormFactory.create_form_cls(
596 permissions)()
597
598 if permissions_form.validate_on_submit():
599
600 try:
601
602
603 permissions.sort(
604 key=lambda x: -1 if x.user_id == flask.g.user.id else 1)
605 for perm in permissions:
606 old_builder = perm.copr_builder
607 old_admin = perm.copr_admin
608 new_builder = permissions_form[
609 "copr_builder_{0}".format(perm.user_id)].data
610 new_admin = permissions_form[
611 "copr_admin_{0}".format(perm.user_id)].data
612 coprs_logic.CoprPermissionsLogic.update_permissions(
613 flask.g.user, copr, perm, new_builder, new_admin)
614 if flask.current_app.config.get("SEND_EMAILS", False) and \
615 (old_builder is not new_builder or old_admin is not new_admin):
616
617 msg = MIMEText(
618 "Your permissions have changed:\n\n"
619 "Builder: {0} -> {1}\nAdmin: {2} -> {3}\n\n"
620 "Project: {4}\nOwner: {5}".format(
621 helpers.PermissionEnum(old_builder),
622 helpers.PermissionEnum(new_builder),
623 helpers.PermissionEnum(old_admin),
624 helpers.PermissionEnum(new_admin),
625 copr.name, copr.user.name))
626
627 msg["Subject"] = "[Copr] {0}: Your permissions have changed".format(copr.name)
628 msg["From"] = "root@{0}".format(platform.node())
629 msg["To"] = perm.user.mail
630 s = smtplib.SMTP("localhost")
631 s.sendmail("root@{0}".format(platform.node()), perm.user.mail, msg.as_string())
632 s.quit()
633
634
635 except exceptions.InsufficientRightsException as e:
636 db.session.rollback()
637 flask.flash(str(e), "error")
638 else:
639 db.session.commit()
640 flask.flash("Project permissions were updated successfully.", "success")
641
642 return flask.redirect(url_for_copr_details(copr))
643
644
645 @coprs_ns.route("/id/<copr_id>/createrepo/", methods=["POST"])
646 @login_required
647 -def copr_createrepo(copr_id):
659
662 form = forms.CoprDeleteForm()
663 if form.validate_on_submit():
664
665 try:
666 ComplexLogic.delete_copr(copr)
667 except (exceptions.ActionInProgressException,
668 exceptions.InsufficientRightsException) as e:
669
670 db.session.rollback()
671 flask.flash(str(e), "error")
672 return flask.redirect(url_on_error)
673 else:
674 db.session.commit()
675 flask.flash("Project has been deleted successfully.")
676 return flask.redirect(url_on_success)
677 else:
678 return render_template("coprs/detail/settings/delete.html", form=form, copr=copr)
679
680
681 @coprs_ns.route("/<username>/<coprname>/delete/", methods=["GET", "POST"])
682 @coprs_ns.route("/g/<group_name>/<coprname>/delete/", methods=["GET", "POST"])
683 @login_required
684 @req_with_copr
685 -def copr_delete(copr):
692
693
694 @coprs_ns.route("/<username>/<coprname>/legal_flag/", methods=["POST"])
695 @coprs_ns.route("/g/<group_name>/<coprname>/legal_flag/", methods=["POST"])
696 @login_required
697 @req_with_copr
698 -def copr_legal_flag(copr):
701
704 form = forms.CoprLegalFlagForm()
705 legal_flag = models.LegalFlag(raise_message=form.comment.data,
706 raised_on=int(time.time()),
707 copr=copr,
708 reporter=flask.g.user)
709 db.session.add(legal_flag)
710 db.session.commit()
711 send_to = app.config["SEND_LEGAL_TO"] or ["root@localhost"]
712 hostname = platform.node()
713 navigate_to = "\nNavigate to http://{0}{1}".format(
714 hostname, flask.url_for("admin_ns.legal_flag"))
715 contact = "\nContact on owner is: {}".format(contact_info)
716 reported_by = "\nReported by {0} <{1}>".format(flask.g.user.name,
717 flask.g.user.mail)
718 try:
719 msg = MIMEText(
720 form.comment.data + navigate_to + contact + reported_by, "plain")
721 except UnicodeEncodeError:
722 msg = MIMEText(form.comment.data.encode(
723 "utf-8") + navigate_to + contact + reported_by, "plain", "utf-8")
724 msg["Subject"] = "Legal flag raised on {0}".format(copr.name)
725 msg["From"] = "root@{0}".format(hostname)
726 msg["To"] = ", ".join(send_to)
727 s = smtplib.SMTP("localhost")
728 s.sendmail("root@{0}".format(hostname), send_to, msg.as_string())
729 s.quit()
730 flask.flash("Admin has been noticed about your report"
731 " and will investigate the project shortly.")
732 return flask.redirect(url_for_copr_details(copr))
733
734
735 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
736 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/<repofile>")
737 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
738 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/<repofile>")
739 -def generate_repo_file(copr_dirname, name_release, repofile, username=None, group_name=None):
746
749 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
750
751 if not mock_chroot:
752 raise ObjectNotFound("Chroot {} does not exist".format(name_release))
753
754 url = os.path.join(copr_dir.repo_url, '')
755 repo_url = generate_repo_url(mock_chroot, url)
756 pubkey_url = urljoin(url, "pubkey.gpg")
757 response = flask.make_response(
758 flask.render_template("coprs/copr_dir.repo", copr_dir=copr_dir, url=repo_url, pubkey_url=pubkey_url))
759 response.mimetype = "text/plain"
760 response.headers["Content-Disposition"] = \
761 "filename={0}.repo".format(copr_dir.repo_name)
762
763 name = REPO_DL_STAT_FMT.format(**{
764 'copr_user': copr_dir.copr.user.name,
765 'copr_project_name': copr_dir.copr.name,
766 'copr_name_release': name_release,
767 })
768 CounterStatLogic.incr(name=name, counter_type=CounterStatType.REPO_DL)
769 db.session.commit()
770
771 return response
772
773
774
775
776
777
778 @coprs_ns.route("/<username>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
779 @coprs_ns.route("/g/<group_name>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
780 @req_with_copr
781 -def generate_module_repo_file(copr, name_release, module_nsv):
784
786 module = ModulesLogic.get_by_nsv_str(copr, module_nsv).one()
787 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
788 url = os.path.join(copr_dir.repo_url, '')
789 repo_url = generate_repo_url(mock_chroot, copr.modules_url)
790 baseurl = "{}+{}/latest/$basearch".format(repo_url.rstrip("/"), module_nsv)
791 pubkey_url = urljoin(url, "pubkey.gpg")
792 response = flask.make_response(
793 flask.render_template("coprs/copr-modules.cfg", copr=copr, module=module,
794 baseurl=baseurl, pubkey_url=pubkey_url))
795 response.mimetype = "text/plain"
796 response.headers["Content-Disposition"] = \
797 "filename={0}.cfg".format(copr.repo_name)
798 return response
799
800
801
802 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>")
803 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
804 try:
805 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages")
806 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm:
807 response = flask.make_response(rpm.read())
808 response.mimetype = "application/x-rpm"
809 response.headers["Content-Disposition"] = \
810 "filename={0}".format(rpmfile)
811 return response
812 except IOError:
813 return flask.render_template("404.html")
814
831
832
833 @coprs_ns.route("/<username>/<coprname>/monitor/")
834 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>")
835 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/")
836 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>")
837 @req_with_copr
838 -def copr_build_monitor(copr, detailed=False):
840
841
842 @coprs_ns.route("/<username>/<coprname>/fork/")
843 @coprs_ns.route("/g/<group_name>/<coprname>/fork/")
844 @login_required
845 @req_with_copr
846 -def copr_fork(copr):
849
852 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
853
854
855 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"])
856 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
857 @login_required
858 @req_with_copr
859 -def copr_fork_post(copr):
860 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)()
861 if form.validate_on_submit():
862 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0]
863 if flask.g.user.name != form.owner.data and not dstgroup:
864 return generic_error("There is no such group: {}".format(form.owner.data))
865
866 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup)
867 if created:
868 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes "
869 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
870 elif not created and form.confirm.data == True:
871 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes "
872 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
873 else:
874 return render_copr_fork(copr, form, confirm=True)
875
876 db.session.commit()
877 flask.flash(msg)
878
879 return flask.redirect(url_for_copr_details(fcopr))
880 return render_copr_fork(copr, form)
881
885 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update_indexes_quick', '1'])
886 return "OK"
887
888
889 @coprs_ns.route("/<username>/<coprname>/modules/")
890 @coprs_ns.route("/g/<group_name>/<coprname>/modules/")
891 @req_with_copr
892 -def copr_modules(copr):
894
899
900
901 @coprs_ns.route("/<username>/<coprname>/create_module/")
902 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/")
903 @login_required
904 @req_with_copr
905 -def copr_create_module(copr):
908
917
918
919 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"])
920 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
921 @login_required
922 @req_with_copr
923 -def copr_create_module_post(copr):
924 form = forms.CreateModuleForm(copr=copr, csrf_enabled=False)
925 args = [copr, form]
926 if "add_profile" in flask.request.values:
927 return add_profile(*args)
928 if "build_module" in flask.request.values:
929 return build_module(*args)
930
939
942 if not form.validate_on_submit():
943
944 for i in range(2, len(form.profile_names)):
945 form.profile_pkgs.append_entry()
946 return render_create_module(copr, form, profiles=len(form.profile_names))
947
948 summary = "Module from Copr repository: {}".format(copr.full_name)
949 generator = ModulemdGenerator(str(copr.name), summary=summary, config=app.config)
950 generator.add_filter(form.filter.data)
951 generator.add_api(form.api.data)
952 generator.add_profiles(enumerate(zip(form.profile_names.data, form.profile_pkgs.data)))
953 generator.add_components(form.packages.data, form.filter.data, form.builds.data)
954 yaml = generator.generate()
955
956 facade = None
957 try:
958 facade = ModuleBuildFacade(flask.g.user, copr, yaml)
959 module = facade.submit_build()
960 db.session.commit()
961
962 flask.flash("Modulemd yaml file successfully generated and submitted to be build as {}"
963 .format(module.nsv), "success")
964 return flask.redirect(url_for_copr_details(copr))
965
966 except ValidationError as ex:
967 flask.flash(ex.message, "error")
968 return render_create_module(copr, form, len(form.profile_names))
969
970 except sqlalchemy.exc.IntegrityError:
971 flask.flash("Module {}-{}-{} already exists".format(
972 facade.modulemd.name, facade.modulemd.stream, facade.modulemd.version), "error")
973 db.session.rollback()
974 return render_create_module(copr, form, len(form.profile_names))
975
976
977 @coprs_ns.route("/<username>/<coprname>/module/<id>")
978 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>")
979 @req_with_copr
980 -def copr_module(copr, id):
998
999
1000 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw")
1001 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw")
1002 @req_with_copr
1003 -def copr_module_raw(copr, id):
1010