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):
158
159
160 @coprs_ns.route("/<username>/new/", methods=["POST"])
161 @coprs_ns.route("/g/<group_name>/new/", methods=["POST"])
162 @login_required
163 -def copr_new(username=None, group_name=None):
164 """
165 Receive information from the user (and group) on how to create its new copr
166 and create it accordingly.
167 """
168 group = None
169 redirect = "coprs/add.html"
170 if group_name:
171 group = ComplexLogic.get_group_by_name_safe(group_name)
172 redirect = "coprs/group_add.html"
173
174 form = forms.CoprFormFactory.create_form_cls(group=group)()
175 if form.validate_on_submit():
176 try:
177 copr = coprs_logic.CoprsLogic.add(
178 flask.g.user,
179 name=form.name.data,
180 homepage=form.homepage.data,
181 contact=form.contact.data,
182 repos=form.repos.data.replace("\n", " "),
183 selected_chroots=form.selected_chroots,
184 description=form.description.data,
185 instructions=form.instructions.data,
186 disable_createrepo=form.disable_createrepo.data,
187 build_enable_net=form.build_enable_net.data,
188 unlisted_on_hp=form.unlisted_on_hp.data,
189 group=group,
190 persistent=form.persistent.data,
191 auto_prune=(form.auto_prune.data if flask.g.user.admin else True),
192 use_bootstrap_container=form.use_bootstrap_container.data,
193 follow_fedora_branching=form.follow_fedora_branching.data,
194 delete_after_days=form.delete_after_days.data,
195 multilib=form.multilib.data,
196 )
197
198 db.session.commit()
199 after_the_project_creation(copr, form)
200 return flask.redirect(url_for_copr_details(copr))
201 except (exceptions.DuplicateException, exceptions.NonAdminCannotCreatePersistentProject) as e:
202 flask.flash(str(e), "error")
203
204 return flask.render_template(redirect, form=form, group=group)
205
208 flask.flash("New project has been created successfully.", "success")
209 _check_rpmfusion(copr.repos)
210 if form.initial_pkgs.data:
211 pkgs = form.initial_pkgs.data.replace("\n", " ").split(" ")
212
213
214 bad_urls = []
215 for pkg in pkgs:
216 if not pkg.endswith(".src.rpm"):
217 bad_urls.append(pkg)
218 flask.flash("Bad url: {0} (skipped)".format(pkg))
219 for bad_url in bad_urls:
220 pkgs.remove(bad_url)
221
222 if not pkgs:
223 flask.flash("No initial packages submitted")
224 else:
225
226 for pkg in pkgs:
227 builds_logic.BuildsLogic.add(
228 flask.g.user,
229 pkgs=pkg,
230 srpm_url=pkg,
231 copr=copr,
232 enable_net=form.build_enable_net.data
233 )
234
235 db.session.commit()
236 flask.flash("Initial packages were successfully submitted "
237 "for building.")
238
239
240 @coprs_ns.route("/<username>/<coprname>/report-abuse")
241 @coprs_ns.route("/g/<group_name>/<coprname>/report-abuse")
242 @req_with_copr
243 @login_required
244 -def copr_report_abuse(copr):
246
251
252
253 @coprs_ns.route("/<username>/<coprname>/")
254 @coprs_ns.route("/g/<group_name>/<coprname>/")
255 @req_with_copr
256 -def copr_detail(copr):
258
261 repo_dl_stat = CounterStatLogic.get_copr_repo_dl_stat(copr)
262 form = forms.CoprLegalFlagForm()
263 repos_info = {}
264 for chroot in copr.active_chroots:
265 chroot_rpms_dl_stat_key = CHROOT_RPMS_DL_STAT_FMT.format(
266 copr_user=copr.owner_name,
267 copr_project_name=copr.name,
268 copr_chroot=chroot.name,
269 )
270 chroot_rpms_dl_stat = TimedStatEvents.get_count(
271 rconnect=rcp.get_connection(),
272 name=chroot_rpms_dl_stat_key,
273 )
274
275 logoset = set()
276 logodir = app.static_folder + "/chroot_logodir"
277 for logo in os.listdir(logodir):
278
279 if fnmatch.fnmatch(logo, "*.png"):
280 logoset.add(logo[:-4])
281
282 if chroot.name_release not in repos_info:
283 logo = None
284 if chroot.name_release in logoset:
285 logo = chroot.name_release + ".png"
286 elif chroot.os_release in logoset:
287 logo = chroot.os_release + ".png"
288
289 repos_info[chroot.name_release] = {
290 "name_release": chroot.name_release,
291 "os_release": chroot.os_release,
292 "os_version": chroot.os_version,
293 "logo": logo,
294 "arch_list": [chroot.arch],
295 "repo_file": "{}-{}.repo".format(copr.repo_id, chroot.name_release),
296 "dl_stat": repo_dl_stat[chroot.name_release],
297 "rpm_dl_stat": {
298 chroot.arch: chroot_rpms_dl_stat
299 }
300 }
301 else:
302 repos_info[chroot.name_release]["arch_list"].append(chroot.arch)
303 repos_info[chroot.name_release]["rpm_dl_stat"][chroot.arch] = chroot_rpms_dl_stat
304
305 if copr.multilib:
306 for name_release in repos_info:
307 arches = repos_info[name_release]['arch_list']
308 arch_repos = {}
309 for ch64, ch32 in models.MockChroot.multilib_pairs.items():
310 if set([ch64, ch32]).issubset(set(arches)):
311 arch_repos[ch64] = ch32
312
313 repos_info[name_release]['arch_repos'] = arch_repos
314
315
316 repos_info_list = sorted(repos_info.values(), key=lambda rec: rec["name_release"])
317 builds = builds_logic.BuildsLogic.get_multiple_by_copr(copr=copr).limit(1).all()
318
319 return flask.render_template(
320 "coprs/detail/overview.html",
321 copr=copr,
322 user=flask.g.user,
323 form=form,
324 repo_dl_stat=repo_dl_stat,
325 repos_info_list=repos_info_list,
326 latest_build=builds[0] if len(builds) == 1 else None,
327 )
328
329
330 @coprs_ns.route("/<username>/<coprname>/permissions/")
331 @coprs_ns.route("/g/<group_name>/<coprname>/permissions/")
332 @req_with_copr
333 -def copr_permissions(copr):
361
364 if not copr.webhook_secret:
365 copr.new_webhook_secret()
366 db.session.add(copr)
367 db.session.commit()
368
369 bitbucket_url = "https://{}/webhooks/bitbucket/{}/{}/".format(
370 app.config["PUBLIC_COPR_HOSTNAME"],
371 copr.id,
372 copr.webhook_secret)
373
374 github_url = "https://{}/webhooks/github/{}/{}/".format(
375 app.config["PUBLIC_COPR_HOSTNAME"],
376 copr.id,
377 copr.webhook_secret)
378
379 gitlab_url = "https://{}/webhooks/gitlab/{}/{}/".format(
380 app.config["PUBLIC_COPR_HOSTNAME"],
381 copr.id,
382 copr.webhook_secret)
383
384 custom_url = "https://{}/webhooks/custom/{}/{}/".format(
385 app.config["PUBLIC_COPR_HOSTNAME"],
386 copr.id,
387 copr.webhook_secret) + "<PACKAGE_NAME>/"
388
389 return flask.render_template(
390 "coprs/detail/settings/integrations.html",
391 copr=copr, bitbucket_url=bitbucket_url, github_url=github_url,
392 gitlab_url=gitlab_url, custom_url=custom_url, pagure_form=pagure_form)
393
394
395 @coprs_ns.route("/<username>/<coprname>/integrations/")
396 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/")
397 @login_required
398 @req_with_copr
399 -def copr_integrations(copr):
412
413
414 @coprs_ns.route("/<username>/<coprname>/integrations/update", methods=["POST"])
415 @coprs_ns.route("/g/<group_name>/<coprname>/integrations/update", methods=["POST"])
416 @login_required
417 @req_with_copr
418 -def copr_integrations_update(copr):
435
447
448
449 @coprs_ns.route("/<username>/<coprname>/edit/")
450 @coprs_ns.route("/g/<group_name>/<coprname>/edit/")
451 @login_required
452 @req_with_copr
453 -def copr_edit(copr, form=None):
455
458 if "rpmfusion" in repos:
459 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>.')
460 flask.flash(message, "error")
461
496
497
498 @coprs_ns.route("/<username>/<coprname>/update/", methods=["POST"])
499 @coprs_ns.route("/g/<group_name>/<coprname>/update/", methods=["POST"])
500 @login_required
501 @req_with_copr
502 -def copr_update(copr):
510
511
512 @coprs_ns.route("/<username>/<coprname>/permissions_applier_change/",
513 methods=["POST"])
514 @coprs_ns.route("/g/<group_name>/<coprname>/permissions_applier_change/", methods=["POST"])
515 @login_required
516 @req_with_copr
517 -def copr_permissions_applier_change(copr):
518 permission = coprs_logic.CoprPermissionsLogic.get(copr, flask.g.user).first()
519 applier_permissions_form = \
520 forms.PermissionsApplierFormFactory.create_form_cls(permission)()
521
522 if copr.user == flask.g.user:
523 flask.flash("Owner cannot request permissions for his own project.", "error")
524 elif applier_permissions_form.validate_on_submit():
525
526 if permission is not None:
527 old_builder = permission.copr_builder
528 old_admin = permission.copr_admin
529 else:
530 old_builder = 0
531 old_admin = 0
532 new_builder = applier_permissions_form.copr_builder.data
533 new_admin = applier_permissions_form.copr_admin.data
534 coprs_logic.CoprPermissionsLogic.update_permissions_by_applier(
535 flask.g.user, copr, permission, new_builder, new_admin)
536 db.session.commit()
537 flask.flash(
538 "Successfully updated permissions for project '{0}'."
539 .format(copr.name))
540
541
542 if flask.current_app.config.get("SEND_EMAILS", False):
543 for mail in copr.admin_mails:
544 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
545 "new_builder": new_builder, "new_admin": new_admin}
546 msg = PermissionRequestMessage(copr, flask.g.user, permission_dict)
547 send_mail([mail], msg)
548
549 return flask.redirect(helpers.copr_url("coprs_ns.copr_detail", copr))
550
551
552 @coprs_ns.route("/<username>/<coprname>/update_permissions/", methods=["POST"])
553 @coprs_ns.route("/g/<group_name>/<coprname>/update_permissions/", methods=["POST"])
554 @login_required
555 @req_with_copr
556 -def copr_update_permissions(copr):
557 permissions = copr.copr_permissions
558 permissions_form = forms.PermissionsFormFactory.create_form_cls(
559 permissions)()
560
561 if permissions_form.validate_on_submit():
562
563 try:
564
565
566 permissions.sort(
567 key=lambda x: -1 if x.user_id == flask.g.user.id else 1)
568 for perm in permissions:
569 old_builder = perm.copr_builder
570 old_admin = perm.copr_admin
571 new_builder = permissions_form[
572 "copr_builder_{0}".format(perm.user_id)].data
573 new_admin = permissions_form[
574 "copr_admin_{0}".format(perm.user_id)].data
575 coprs_logic.CoprPermissionsLogic.update_permissions(
576 flask.g.user, copr, perm, new_builder, new_admin)
577 if flask.current_app.config.get("SEND_EMAILS", False) and \
578 (old_builder is not new_builder or old_admin is not new_admin):
579 permission_dict = {"old_builder": old_builder, "old_admin": old_admin,
580 "new_builder": new_builder, "new_admin": new_admin}
581 msg = PermissionChangeMessage(copr, permission_dict)
582 send_mail(perm.user.mail, msg)
583
584
585 except exceptions.InsufficientRightsException as e:
586 db.session.rollback()
587 flask.flash(str(e), "error")
588 else:
589 db.session.commit()
590 flask.flash("Project permissions were updated successfully.", "success")
591
592 return flask.redirect(url_for_copr_details(copr))
593
594
595 @coprs_ns.route("/<username>/<coprname>/repositories/")
596 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/")
597 @login_required
598 @req_with_copr
599 -def copr_repositories(copr):
605
611
612
613 @coprs_ns.route("/<username>/<coprname>/repositories/", methods=["POST"])
614 @coprs_ns.route("/g/<group_name>/<coprname>/repositories/", methods=["POST"])
615 @login_required
616 @req_with_copr
617 -def copr_repositories_post(copr):
618 if not flask.g.user.can_edit(copr):
619 flask.flash("You don't have access to this page.", "error")
620 return flask.redirect(url_for_copr_details(copr))
621
622 form = forms.CoprChrootExtend()
623 if form.extend.data:
624 delete_after_days = app.config["DELETE_EOL_CHROOTS_AFTER"] + 1
625 chroot_name = form.extend.data
626 flask.flash("Repository for {} will be preserved for another {} days from now"
627 .format(chroot_name, app.config["DELETE_EOL_CHROOTS_AFTER"]))
628 elif form.expire.data:
629 delete_after_days = 0
630 chroot_name = form.expire.data
631 flask.flash("Repository for {} is scheduled to be removed."
632 "If you changed your mind, click 'Extend` to revert your decision."
633 .format(chroot_name))
634 else:
635 raise ValidationError("Copr chroot needs to be either extended or expired")
636
637 copr_chroot = coprs_logic.CoprChrootsLogic.get_by_name(copr, chroot_name, active_only=False).one()
638 delete_after_timestamp = datetime.datetime.now() + datetime.timedelta(days=delete_after_days)
639 coprs_logic.CoprChrootsLogic.update_chroot(flask.g.user, copr_chroot,
640 delete_after=delete_after_timestamp)
641 db.session.commit()
642 return render_copr_repositories(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):
700
718
719
720 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
721 @coprs_ns.route("/<username>/<copr_dirname>/repo/<name_release>/<repofile>")
722 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/", defaults={"repofile": None})
723 @coprs_ns.route("/g/<group_name>/<copr_dirname>/repo/<name_release>/<repofile>")
724 @req_with_copr_dir
725 -def generate_repo_file(copr_dir, name_release, repofile):
731
734 repo_id = "copr:{0}:{1}:{2}{3}".format(
735 app.config["PUBLIC_COPR_HOSTNAME"].split(":")[0],
736 copr_dir.copr.owner_name.replace("@", "group_"),
737 copr_dir.name,
738 ":ml" if arch else ""
739 )
740 url = os.path.join(copr_dir.repo_url, '')
741 repo_url = generate_repo_url(mock_chroot, url, arch)
742 pubkey_url = urljoin(url, "pubkey.gpg")
743 return flask.render_template("coprs/copr_dir.repo", copr_dir=copr_dir,
744 url=repo_url, pubkey_url=pubkey_url,
745 repo_id=repo_id) + "\n"
746
792
793
794
795
796
797
798 @coprs_ns.route("/<username>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
799 @coprs_ns.route("/g/<group_name>/<coprname>/module_repo/<name_release>/<module_nsv>.repo")
800 @req_with_copr
801 -def generate_module_repo_file(copr, name_release, module_nsv):
804
806 module = ModulesLogic.get_by_nsv_str(copr, module_nsv).one()
807 mock_chroot = coprs_logic.MockChrootsLogic.get_from_name(name_release, noarch=True).first()
808 url = os.path.join(copr.main_dir.repo_url, '')
809 repo_url = generate_repo_url(mock_chroot, copr.modules_url)
810 baseurl = "{}+{}/latest/$basearch".format(repo_url.rstrip("/"), module_nsv)
811 pubkey_url = urljoin(url, "pubkey.gpg")
812 response = flask.make_response(
813 flask.render_template("coprs/copr-modules.cfg", copr=copr, module=module,
814 baseurl=baseurl, pubkey_url=pubkey_url))
815 response.mimetype = "text/plain"
816 response.headers["Content-Disposition"] = \
817 "filename={0}.cfg".format(copr.repo_name)
818 return response
819
820
821
822 @coprs_ns.route("/<username>/<coprname>/rpm/<name_release>/<rpmfile>")
823 -def copr_repo_rpm_file(username, coprname, name_release, rpmfile):
824 try:
825 packages_dir = os.path.join(app.config["DATA_DIR"], "repo-rpm-packages")
826 with open(os.path.join(packages_dir, rpmfile), "rb") as rpm:
827 response = flask.make_response(rpm.read())
828 response.mimetype = "application/x-rpm"
829 response.headers["Content-Disposition"] = \
830 "filename={0}".format(rpmfile)
831 return response
832 except IOError:
833 return flask.render_template("404.html")
834
851
852
853 @coprs_ns.route("/<username>/<coprname>/monitor/")
854 @coprs_ns.route("/<username>/<coprname>/monitor/<detailed>")
855 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/")
856 @coprs_ns.route("/g/<group_name>/<coprname>/monitor/<detailed>")
857 @req_with_copr
858 -def copr_build_monitor(copr, detailed=False):
860
861
862 @coprs_ns.route("/<username>/<coprname>/fork/")
863 @coprs_ns.route("/g/<group_name>/<coprname>/fork/")
864 @login_required
865 @req_with_copr
866 -def copr_fork(copr):
869
872 return flask.render_template("coprs/fork.html", copr=copr, form=form, confirm=confirm)
873
874
875 @coprs_ns.route("/<username>/<coprname>/fork/", methods=["POST"])
876 @coprs_ns.route("/g/<group_name>/<coprname>/fork/", methods=["POST"])
877 @login_required
878 @req_with_copr
879 -def copr_fork_post(copr):
880 form = forms.CoprForkFormFactory.create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)()
881 if form.validate_on_submit():
882 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0]
883 if flask.g.user.name != form.owner.data and not dstgroup:
884 return generic_error("There is no such group: {}".format(form.owner.data))
885
886 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup)
887 if created:
888 msg = ("Forking project {} for you into {}. Please be aware that it may take a few minutes "
889 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
890 elif not created and form.confirm.data == True:
891 msg = ("Updating packages in {} from {}. Please be aware that it may take a few minutes "
892 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
893 else:
894 return render_copr_fork(copr, form, confirm=True)
895
896 db.session.commit()
897 flask.flash(msg)
898
899 return flask.redirect(url_for_copr_details(fcopr))
900 return render_copr_fork(copr, form)
901
902
903 @coprs_ns.route("/<username>/<coprname>/forks/")
904 @coprs_ns.route("/g/<group_name>/<coprname>/forks/")
905 @req_with_copr
906 -def copr_forks(copr):
907 return flask.render_template("coprs/detail/forks.html", copr=copr)
908
912 subprocess.call(['/usr/share/copr/coprs_frontend/manage.py', 'update-indexes-quick', '1'])
913 return "OK"
914
915
916 @coprs_ns.route("/<username>/<coprname>/modules/")
917 @coprs_ns.route("/g/<group_name>/<coprname>/modules/")
918 @req_with_copr
919 -def copr_modules(copr):
921
926
927
928 @coprs_ns.route("/<username>/<coprname>/create_module/")
929 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/")
930 @login_required
931 @req_with_copr
932 -def copr_create_module(copr):
935
944
945
946 @coprs_ns.route("/<username>/<coprname>/create_module/", methods=["POST"])
947 @coprs_ns.route("/g/<group_name>/<coprname>/create_module/", methods=["POST"])
948 @login_required
949 @req_with_copr
950 -def copr_create_module_post(copr):
951 form = forms.CreateModuleForm(copr=copr, meta={'csrf': False})
952 args = [copr, form]
953 if "add_profile" in flask.request.values:
954 return add_profile(*args)
955 if "build_module" in flask.request.values:
956 return build_module(*args)
957
966
969 if not form.validate_on_submit():
970
971 for i in range(2, len(form.profile_names)):
972 form.profile_pkgs.append_entry()
973 return render_create_module(copr, form, profiles=len(form.profile_names))
974
975 summary = "Module from Copr repository: {}".format(copr.full_name)
976 generator = ModulemdGenerator(str(copr.name), summary=summary, config=app.config)
977 generator.add_filter(form.filter.data)
978 generator.add_api(form.api.data)
979 generator.add_profiles(enumerate(zip(form.profile_names.data, form.profile_pkgs.data)))
980 generator.add_components(form.packages.data, form.filter.data, form.builds.data)
981 yaml = generator.generate()
982
983 facade = None
984 try:
985 facade = ModuleBuildFacade(flask.g.user, copr, yaml)
986 module = facade.submit_build()
987 db.session.commit()
988
989 flask.flash("Modulemd yaml file successfully generated and submitted to be build as {}"
990 .format(module.nsv), "success")
991 return flask.redirect(url_for_copr_details(copr))
992
993 except ValidationError as ex:
994 flask.flash(ex.message, "error")
995 return render_create_module(copr, form, len(form.profile_names))
996
997 except sqlalchemy.exc.IntegrityError:
998 flask.flash("Module {}-{}-{} already exists".format(
999 facade.modulemd.name, facade.modulemd.stream, facade.modulemd.version), "error")
1000 db.session.rollback()
1001 return render_create_module(copr, form, len(form.profile_names))
1002
1003
1004 @coprs_ns.route("/<username>/<coprname>/module/<id>")
1005 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>")
1006 @req_with_copr
1007 -def copr_module(copr, id):
1025
1026
1027 @coprs_ns.route("/<username>/<coprname>/module/<id>/raw")
1028 @coprs_ns.route("/g/<group_name>/<coprname>/module/<id>/raw")
1029 @req_with_copr
1030 -def copr_module_raw(copr, id):
1037