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
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, req_with_copr, generic_error
40
41 from coprs.views.coprs_ns import coprs_ns
42
43 from coprs.logic import builds_logic, coprs_logic, actions_logic, users_logic
44 from coprs.helpers import generate_repo_url, CHROOT_RPMS_DL_STAT_FMT, \
45 url_for_copr_view, REPO_DL_STAT_FMT, CounterStatType
52
59
60
61 @coprs_ns.route("/", defaults={"page": 1})
62 @coprs_ns.route("/<int:page>/")
63 -def coprs_show(page=1):
84
85
86 @coprs_ns.route("/<username>/", defaults={"page": 1})
87 @coprs_ns.route("/<username>/<int:page>/")
88 -def coprs_by_user(username=None, page=1):
89 user = users_logic.UsersLogic.get(username).first()
90 if not user:
91 return page_not_found(
92 "User {0} does not exist.".format(username))
93
94 query = CoprsLogic.get_multiple_owned_by_username(username)
95 query = CoprsLogic.filter_without_group_projects(query)
96 query = CoprsLogic.set_query_order(query, desc=True)
97
98 paginator = helpers.Paginator(query, query.count(), page)
99
100 coprs = paginator.sliced_query
101
102
103 users_builds = builds_logic.BuildsLogic.get_recent_tasks(flask.g.user, 4)
104
105 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
106
107 return flask.render_template("coprs/show/user.html",
108 user=user,
109 coprs=coprs,
110 paginator=paginator,
111 tasks_info=ComplexLogic.get_queue_sizes(),
112 users_builds=users_builds,
113 graph=data)
114
115
116 @coprs_ns.route("/fulltext/", defaults={"page": 1})
117 @coprs_ns.route("/fulltext/<int:page>/")
118 -def coprs_fulltext_search(page=1):
119 fulltext = flask.request.args.get("fulltext", "")
120 try:
121 query = coprs_logic.CoprsLogic.get_multiple_fulltext(fulltext)
122 except ValueError as e:
123 flask.flash(str(e), "error")
124 return flask.redirect(flask.request.referrer or
125 flask.url_for("coprs_ns.coprs_show"))
126
127 paginator = helpers.Paginator(query, query.count(), page,
128 additional_params={"fulltext": fulltext})
129
130 data = builds_logic.BuildsLogic.get_small_graph_data('30min')
131
132 coprs = paginator.sliced_query
133 return render_template("coprs/show/fulltext.html",
134 coprs=coprs,
135 paginator=paginator,
136 fulltext=fulltext,
137 tasks_info=ComplexLogic.get_queue_sizes(),
138 graph=data)
139
140
141 @coprs_ns.route("/<username>/add/")
142 @coprs_ns.route("/g/<group_name>/add/")
143 @login_required
144 -def copr_add(username=None, group_name=None):
150
151
152 @coprs_ns.route("/<username>/new/", methods=["POST"])
153 @coprs_ns.route("/g/<group_name>/new/", methods=["POST"])
154 @login_required
155 -def copr_new(username=None, group_name=None):
157
160 """
161 Receive information from the user (and group) on how to create its new copr
162 and create it accordingly.
163 """
164 group = None
165 redirect = "coprs/add.html"
166 if group_name:
167 group = ComplexLogic.get_group_by_name_safe(group_name)
168 redirect = "coprs/group_add.html"
169
170 form = forms.CoprFormFactory.create_form_cls(group=group)()
171 if form.validate_on_submit():
172 try:
173 copr = coprs_logic.CoprsLogic.add(
174 flask.g.user,
175 name=form.name.data,
176 homepage=form.homepage.data,
177 contact=form.contact.data,
178 repos=form.repos.data.replace("\n", " "),
179 selected_chroots=form.selected_chroots,
180 description=form.description.data,
181 instructions=form.instructions.data,
182 disable_createrepo=form.disable_createrepo.data,
183 build_enable_net=form.build_enable_net.data,
184 unlisted_on_hp=form.unlisted_on_hp.data,
185 group=group,
186 persistent=form.persistent.data,
187 auto_prune=(form.auto_prune.data if flask.g.user.admin else True),
188 use_bootstrap_container=form.use_bootstrap_container.data,
189 follow_fedora_branching=form.follow_fedora_branching.data,
190 delete_after_days=form.delete_after_days.data,
191 )
192 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e:
193 flask.flash(str(e), "error")
194 return flask.render_template(redirect, form=form)
195
196 db.session.commit()
197 after_the_project_creation(copr, form)
198
199 return flask.redirect(url_for_copr_details(copr))
200 else:
201 return flask.render_template(redirect, form=form)
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 repos_info_list = sorted(repos_info.values(), key=lambda rec: rec["name_release"])
302 builds = builds_logic.BuildsLogic.get_multiple_by_copr(copr=copr).limit(1).all()
303
304 return flask.render_template(
305 "coprs/detail/overview.html",
306 copr=copr,
307 user=flask.g.user,
308 form=form,
309 repo_dl_stat=repo_dl_stat,
310 repos_info_list=repos_info_list,
311 latest_build=builds[0] if len(builds) == 1 else None,
312 )
313
314
315 @coprs_ns.route("/<username>/<coprname>/permissions/")
316 @req_with_copr
317 -def copr_permissions(copr):
345
348 if not copr.webhook_secret:
349 copr.new_webhook_secret()
350 db.session.add(copr)
351 db.session.commit()
352
353 bitbucket_url = "https://{}/webhooks/bitbucket/{}/{}/".format(
354 app.config["PUBLIC_COPR_HOSTNAME"],
355 copr.id,
356 copr.webhook_secret)
357
358 github_url = "https://{}/webhooks/github/{}/{}/".format(
359 app.config["PUBLIC_COPR_HOSTNAME"],
360 copr.id,
361 copr.webhook_secret)
362
363 gitlab_url = "https://{}/webhooks/gitlab/{}/{}/".format(
364 app.config["PUBLIC_COPR_HOSTNAME"],
365 copr.id,
366 copr.webhook_secret)
367
368 custom_url = "https://{}/webhooks/custom/{}/{}/".format(
369 app.config["PUBLIC_COPR_HOSTNAME"],
370 copr.id,
371 copr.webhook_secret) + "<PACKAGE_NAME>/"
372
373 return flask.render_template(
374 "coprs/detail/settings/integrations.html",
375 copr=copr, bitbucket_url=bitbucket_url, github_url=github_url,
376 gitlab_url=gitlab_url, custom_url=custom_url, pagure_form=pagure_form)
377
378
379 @coprs_ns.route("/<username>/<coprname>/integrations/")
380 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/")
381 @login_required
382 @req_with_copr
383 -def copr_integrations(copr):
396
397
398 @coprs_ns.route("/<username>/<coprname>/integrations/update", methods=["POST"])
399 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/update", methods=["POST"])
400 @login_required
401 @req_with_copr
402 -def copr_integrations_update(copr):
419
428
429
430 @coprs_ns.route("/<username>/<coprname>/edit/")
431 @coprs_ns.route("/g/<group_name>/<coprname>/edit/")
432 @login_required
433 @req_with_copr
434 -def copr_edit(copr, form=None):
436
439 if "rpmfusion" in repos:
440 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>.')
441 flask.flash(message, "error")
442
475
476
477 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
478 @coprs_ns.route("/g/<group_name>/<coprname>/update/", methods=["POST"])
479 @login_required
480 @req_with_copr
481 -def copr_update(copr):
489
490
491 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/",
492 methods=["POST"])
496 permission = coprs_logic.CoprPermissionsLogic.get(copr, flask.g.user).first()
497 applier_permissions_form = \
498 forms.PermissionsApplierFormFactory.create_form_cls(permission)()
499
500 if copr.user == flask.g.user:
501 flask.flash("Owner cannot request permissions for his own project.", "error")
502 elif applier_permissions_form.validate_on_submit():
503
504 if permission is not None:
505 old_builder = permission.copr_builder
506 old_admin = permission.copr_admin
507 else:
508 old_builder = 0
509 old_admin = 0
510 new_builder = applier_permissions_form.copr_builder.data
511 new_admin = applier_permissions_form.copr_admin.data
512 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier(
513 flask.g.user, copr, permission, new_builder, new_admin)
514 db.session.commit()
515 flask.flash(
516 "Successfully updated permissions for project '{0}'."
517 .format(copr.name))
518
519
520 if flask.current_app.config.get("SEND_EMAILS", False):
521 for mail in copr.admin_mails:
522 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
523 "new_builder": new_builder, "new_admin": new_admin}
524 msg = PermissionRequestMessage(copr, flask.g.user, permission_dict)
525 send_mail(mail, msg, )
526
527 return flask.redirect(flask.url_for("coprs_ns.copr_detail",
528 username=copr.user.name,
529 coprname=copr.name))
530
531
532 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
533 @login_required
534 @req_with_copr
535 -def copr_update_permissions(copr):
536 permissions = copr.copr_permissions
537 permissions_form = forms.PermissionsFormFactory.create_form_cls(
538 permissions)()
539
540 if permissions_form.validate_on_submit():
541
542 try:
543
544
545 permissions.sort(
546 key=lambda x: -1 if x.user_id == flask.g.user.id else 1)
547 for perm in permissions:
548 old_builder = perm.copr_builder
549 old_admin = perm.copr_admin
550 new_builder = permissions_form[
551 "copr_builder_{0}".format(perm.user_id)].data
552 new_admin = permissions_form[
553 "copr_admin_{0}".format(perm.user_id)].data
554 coprs_logic.CoprPermissionsLogic.update_permissions(
555 flask.g.user, copr, perm, new_builder, new_admin)
556 if flask.current_app.config.get("SEND_EMAILS", False) and \
557 (old_builder is not new_builder or old_admin is not new_admin):
558 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
559 "new_builder": new_builder, "new_admin": new_admin}
560 msg = PermissionChangeMessage(copr, permission_dict)
561 send_mail(perm.user.mail, msg)
562
563
564 except exceptions.InsufficientRightsException as e:
565 db.session.rollback()
566 flask.flash(str(e), "error")
567 else:
568 db.session.commit()
569 flask.flash("Project permissions were updated successfully.", "success")
570
571 return flask.redirect(url_for_copr_details(copr))
572
573
574 @coprs_ns.route("/<username>/<coprname>/repositories/")
575 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/")
576 @login_required
577 @req_with_copr
578 -def copr_repositories(copr):
584
590
591
592 @coprs_ns.route("/<username>/<coprname>/repositories/", methods=["POST"])
593 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/", methods=["POST"])
594 @login_required
595 @req_with_copr
596 -def copr_repositories_post(copr):
597 if not flask.g.user.can_edit(copr):
598 flask.flash("You don't have access to this page.", "error")
599 return flask.redirect(url_for_copr_details(copr))
600
601 form = forms.CoprChrootExtend()
602 if form.extend.data:
603 delete_after_days = app.config["DELETE_EOL_CHROOTS_AFTER"] + 1
604 chroot_name = form.extend.data
605 flask.flash("Repository for {} will be preserved for another {} days from now"
606 .format(chroot_name, app.config["DELETE_EOL_CHROOTS_AFTER"]))
607 elif form.expire.data:
608 delete_after_days = 0
609 chroot_name = form.expire.data
610 flask.flash("Repository for {} is scheduled to be removed."
611 "If you changed your mind, click 'Extend` to revert your decision."
612 .format(chroot_name))
613 else:
614 raise ValidationError("Copr chroot needs to be either extended or expired")
615
616 copr_chroot = coprs_logic.CoprChrootsLogic.get_by_name(copr, chroot_name, active_only=False).one()
617 delete_after_timestamp = datetime.datetime.now() + datetime.timedelta(days=delete_after_days)
618 coprs_logic.CoprChrootsLogic.update_chroot(flask.g.user, copr_chroot,
619 delete_after=delete_after_timestamp)
620 db.session.commit()
621 return render_copr_repositories(copr)
622
623
624 @coprs_ns.route("/id/<copr_id>/createrepo/", methods=["POST"])
625 @login_required
626 -def copr_createrepo(copr_id):
638
641 form = forms.CoprDeleteForm()
642 if form.validate_on_submit():
643
644 try:
645 ComplexLogic.delete_copr(copr)
646 except (exceptions.ActionInProgressException,
647 exceptions.InsufficientRightsException) as e:
648
649 db.session.rollback()
650 flask.flash(str(e), "error")
651 return flask.redirect(url_on_error)
652 else:
653 db.session.commit()
654 flask.flash("Project has been deleted successfully.")
655 return flask.redirect(url_on_success)
656 else:
657 return render_template("coprs/detail/settings/delete.html", form=form, copr=copr)
658
659
660 @coprs_ns.route("/<username>/<coprname>/delete/", methods=["GET", "POST"])
661 @coprs_ns.route("/g/<group_name>/<coprname>/delete/", methods=["GET", "POST"])
662 @login_required
663 @req_with_copr
664 -def copr_delete(copr):
671
672
673 @coprs_ns.route("/<username>/<coprname>/legal_flag/", methods=["POST"])
674 @coprs_ns.route("/g/<group_name>/<coprname>/legal_flag/", methods=["POST"])
675 @login_required
676 @req_with_copr
677 -def copr_legal_flag(copr):
679
697
698
699 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
700 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/<repofile>")
701 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
702 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/<repofile>")
703 -def generate_repo_file(copr_dirname, name_release, repofile, username=None, group_name=None):
710
713 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
714
715 if not mock_chroot:
716 raise ObjectNotFound("Chroot {} does not exist".format(name_release))
717
718 repo_id = helpers.get_copr_repo_id(copr_dir)
719 url = os.path.join(copr_dir.repo_url, '')
720 repo_url = generate_repo_url(mock_chroot, url)
721 pubkey_url = urljoin(url, "pubkey.gpg")
722 response = flask.make_response(
723 flask.render_template("coprs/copr_dir.repo", copr_dir=copr_dir, url=repo_url, pubkey_url=pubkey_url,
724 repo_id=repo_id))
725 response.mimetype = "text/plain"
726 response.headers["Content-Disposition"] = \
727 "filename={0}.repo".format(copr_dir.repo_name)
728
729 name = REPO_DL_STAT_FMT.format(**{
730 'copr_user': copr_dir.copr.user.name,
731 'copr_project_name': copr_dir.copr.name,
732 'copr_name_release': name_release,
733 })
734 CounterStatLogic.incr(name=name, counter_type=CounterStatType.REPO_DL)
735 db.session.commit()
736
737 return response
738
739
740
741
742
743
744 @coprs_ns.route("/<username>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
745 @coprs_ns.route("/g/<group_name>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
746 @req_with_copr
747 -def generate_module_repo_file(copr, name_release, module_nsv):
750
752 module = ModulesLogic.get_by_nsv_str(copr, module_nsv).one()
753 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
754 url = os.path.join(copr.main_dir.repo_url, '')
755 repo_url = generate_repo_url(mock_chroot, copr.modules_url)
756 baseurl = "{}+{}/latest/$basearch".format(repo_url.rstrip("/"), module_nsv)
757 pubkey_url = urljoin(url, "pubkey.gpg")
758 response = flask.make_response(
759 flask.render_template("coprs/copr-modules.cfg", copr=copr, module=module,
760 baseurl=baseurl, pubkey_url=pubkey_url))
761 response.mimetype = "text/plain"
762 response.headers["Content-Disposition"] = \
763 "filename={0}.cfg".format(copr.repo_name)
764 return response
765
766
767
768 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>")
769 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
770 try:
771 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages")
772 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm:
773 response = flask.make_response(rpm.read())
774 response.mimetype = "application/x-rpm"
775 response.headers["Content-Disposition"] = \
776 "filename={0}".format(rpmfile)
777 return response
778 except IOError:
779 return flask.render_template("404.html")
780
797
798
799 @coprs_ns.route("/<username>/<coprname>/monitor/")
800 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>")
801 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/")
802 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>")
803 @req_with_copr
804 -def copr_build_monitor(copr, detailed=False):
806
807
808 @coprs_ns.route("/<username>/<coprname>/fork/")
809 @coprs_ns.route("/g/<group_name>/<coprname>/fork/")
810 @login_required
811 @req_with_copr
812 -def copr_fork(copr):
815
818 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
819
820
821 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"])
822 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
823 @login_required
824 @req_with_copr
825 -def copr_fork_post(copr):
826 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)()
827 if form.validate_on_submit():
828 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0]
829 if flask.g.user.name != form.owner.data and not dstgroup:
830 return generic_error("There is no such group: {}".format(form.owner.data))
831
832 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup)
833 if created:
834 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes "
835 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
836 elif not created and form.confirm.data == True:
837 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes "
838 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
839 else:
840 return render_copr_fork(copr, form, confirm=True)
841
842 db.session.commit()
843 flask.flash(msg)
844
845 return flask.redirect(url_for_copr_details(fcopr))
846 return render_copr_fork(copr, form)
847
848
849 @coprs_ns.route("/<username>/<coprname>/forks/")
850 @coprs_ns.route("/g/<group_name>/<coprname>/forks/")
851 @req_with_copr
852 -def copr_forks(copr):
853 return flask.render_template("coprs/detail/forks.html", copr=copr)
854
858 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update_indexes_quick', '1'])
859 return "OK"
860
861
862 @coprs_ns.route("/<username>/<coprname>/modules/")
863 @coprs_ns.route("/g/<group_name>/<coprname>/modules/")
864 @req_with_copr
865 -def copr_modules(copr):
867
872
873
874 @coprs_ns.route("/<username>/<coprname>/create_module/")
875 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/")
876 @login_required
877 @req_with_copr
878 -def copr_create_module(copr):
881
890
891
892 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"])
893 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
894 @login_required
895 @req_with_copr
896 -def copr_create_module_post(copr):
897 form = forms.CreateModuleForm(copr=copr, meta={'csrf': False})
898 args = [copr, form]
899 if "add_profile" in flask.request.values:
900 return add_profile(*args)
901 if "build_module" in flask.request.values:
902 return build_module(*args)
903
912
915 if not form.validate_on_submit():
916
917 for i in range(2, len(form.profile_names)):
918 form.profile_pkgs.append_entry()
919 return render_create_module(copr, form, profiles=len(form.profile_names))
920
921 summary = "Module from Copr repository: {}".format(copr.full_name)
922 generator = ModulemdGenerator(str(copr.name), summary=summary, config=app.config)
923 generator.add_filter(form.filter.data)
924 generator.add_api(form.api.data)
925 generator.add_profiles(enumerate(zip(form.profile_names.data, form.profile_pkgs.data)))
926 generator.add_components(form.packages.data, form.filter.data, form.builds.data)
927 yaml = generator.generate()
928
929 facade = None
930 try:
931 facade = ModuleBuildFacade(flask.g.user, copr, yaml)
932 module = facade.submit_build()
933 db.session.commit()
934
935 flask.flash("Modulemd yaml file successfully generated and submitted to be build as {}"
936 .format(module.nsv), "success")
937 return flask.redirect(url_for_copr_details(copr))
938
939 except ValidationError as ex:
940 flask.flash(ex.message, "error")
941 return render_create_module(copr, form, len(form.profile_names))
942
943 except sqlalchemy.exc.IntegrityError:
944 flask.flash("Module {}-{}-{} already exists".format(
945 facade.modulemd.name, facade.modulemd.stream, facade.modulemd.version), "error")
946 db.session.rollback()
947 return render_create_module(copr, form, len(form.profile_names))
948
949
950 @coprs_ns.route("/<username>/<coprname>/module/<id>")
951 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>")
952 @req_with_copr
953 -def copr_module(copr, id):
971
972
973 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw")
974 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw")
975 @req_with_copr
976 -def copr_module_raw(copr, id):
983