1 import base64
2 import datetime
3 from functools import wraps
4 import os
5 import flask
6 import sqlalchemy
7 import json
8 import requests
9 from requests.exceptions import RequestException, InvalidSchema
10 from wtforms import ValidationError
11
12 from werkzeug import secure_filename
13
14 from coprs import db
15 from coprs import exceptions
16 from coprs import forms
17 from coprs import helpers
18 from coprs import models
19 from coprs.helpers import fix_protocol_for_backend, generate_build_config
20 from coprs.logic.api_logic import MonitorWrapper
21 from coprs.logic.builds_logic import BuildsLogic
22 from coprs.logic.complex_logic import ComplexLogic
23 from coprs.logic.users_logic import UsersLogic
24 from coprs.logic.packages_logic import PackagesLogic
25 from coprs.logic.modules_logic import ModulesLogic, ModuleProvider, ModuleBuildFacade
26
27 from coprs.views.misc import login_required, api_login_required
28
29 from coprs.views.api_ns import api_ns
30
31 from coprs.logic import builds_logic
32 from coprs.logic import coprs_logic
33 from coprs.logic.coprs_logic import CoprsLogic
34 from coprs.logic.actions_logic import ActionsLogic
35
36 from coprs.exceptions import (ActionInProgressException,
37 InsufficientRightsException,
38 DuplicateException,
39 LegacyApiError,
40 NoPackageSourceException,
41 UnknownSourceTypeException)
54 return wrapper
55
59 """
60 Render the home page of the api.
61 This page provides information on how to call/use the API.
62 """
63
64 return flask.render_template("api.html")
65
66
67 @api_ns.route("/new/", methods=["GET", "POST"])
68 @login_required
69 -def api_new_token():
88
91 infos = []
92
93
94 proxyuser_keys = ["username"]
95 allowed = list(form.__dict__.keys()) + proxyuser_keys
96 for post_key in flask.request.form.keys():
97 if post_key not in allowed:
98 infos.append("Unknown key '{key}' received.".format(key=post_key))
99 return infos
100
101
102 @api_ns.route("/status")
103 -def api_status():
113
114
115 @api_ns.route("/coprs/<username>/new/", methods=["POST"])
116 @api_login_required
117 -def api_new_copr(username):
118 """
119 Receive information from the user on how to create its new copr,
120 check their validity and create the corresponding copr.
121
122 :arg name: the name of the copr to add
123 :arg chroots: a comma separated list of chroots to use
124 :kwarg repos: a comma separated list of repository that this copr
125 can use.
126 :kwarg initial_pkgs: a comma separated list of initial packages to
127 build in this new copr
128
129 """
130
131 form = forms.CoprFormFactory.create_form_cls()(csrf_enabled=False)
132 infos = []
133
134
135 infos.extend(validate_post_keys(form))
136
137 if form.validate_on_submit():
138 group = ComplexLogic.get_group_by_name_safe(username[1:]) if username[0] == "@" else None
139
140 auto_prune = True
141 if "auto_prune" in flask.request.form:
142 auto_prune = form.auto_prune.data
143
144 use_bootstrap_container = True
145 if "use_bootstrap_container" in flask.request.form:
146 use_bootstrap_container = form.use_bootstrap_container.data
147
148 try:
149 copr = CoprsLogic.add(
150 name=form.name.data.strip(),
151 repos=" ".join(form.repos.data.split()),
152 user=flask.g.user,
153 selected_chroots=form.selected_chroots,
154 description=form.description.data,
155 instructions=form.instructions.data,
156 check_for_duplicates=True,
157 disable_createrepo=form.disable_createrepo.data,
158 unlisted_on_hp=form.unlisted_on_hp.data,
159 build_enable_net=form.build_enable_net.data,
160 group=group,
161 persistent=form.persistent.data,
162 auto_prune=auto_prune,
163 use_bootstrap_container=use_bootstrap_container,
164 )
165 infos.append("New project was successfully created.")
166
167 if form.initial_pkgs.data:
168 pkgs = form.initial_pkgs.data.split()
169 for pkg in pkgs:
170 builds_logic.BuildsLogic.add(
171 user=flask.g.user,
172 pkgs=pkg,
173 srpm_url=pkg,
174 copr=copr)
175
176 infos.append("Initial packages were successfully "
177 "submitted for building.")
178
179 output = {"output": "ok", "message": "\n".join(infos)}
180 db.session.commit()
181 except (exceptions.DuplicateException,
182 exceptions.NonAdminCannotCreatePersistentProject,
183 exceptions.NonAdminCannotDisableAutoPrunning) as err:
184 db.session.rollback()
185 raise LegacyApiError(str(err))
186
187 else:
188 errormsg = "Validation error\n"
189 if form.errors:
190 for field, emsgs in form.errors.items():
191 errormsg += "- {0}: {1}\n".format(field, "\n".join(emsgs))
192
193 errormsg = errormsg.replace('"', "'")
194 raise LegacyApiError(errormsg)
195
196 return flask.jsonify(output)
197
198
199 @api_ns.route("/coprs/<username>/<coprname>/delete/", methods=["POST"])
200 @api_login_required
201 @api_req_with_copr
202 -def api_copr_delete(copr):
224
225
226 @api_ns.route("/coprs/<username>/<coprname>/fork/", methods=["POST"])
227 @api_login_required
228 @api_req_with_copr
229 -def api_copr_fork(copr):
230 """ Fork the project and builds in it
231 """
232 form = forms.CoprForkFormFactory\
233 .create_form_cls(copr=copr, user=flask.g.user, groups=flask.g.user.user_groups)(csrf_enabled=False)
234
235 if form.validate_on_submit() and copr:
236 try:
237 dstgroup = ([g for g in flask.g.user.user_groups if g.at_name == form.owner.data] or [None])[0]
238 if flask.g.user.name != form.owner.data and not dstgroup:
239 return LegacyApiError("There is no such group: {}".format(form.owner.data))
240
241 fcopr, created = ComplexLogic.fork_copr(copr, flask.g.user, dstname=form.name.data, dstgroup=dstgroup)
242 if created:
243 msg = ("Forking project {} for you into {}.\nPlease be aware that it may take a few minutes "
244 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
245 elif not created and form.confirm.data == True:
246 msg = ("Updating packages in {} from {}.\nPlease be aware that it may take a few minutes "
247 "to duplicate backend data.".format(copr.full_name, fcopr.full_name))
248 else:
249 raise LegacyApiError("You are about to fork into existing project: {}\n"
250 "Please use --confirm if you really want to do this".format(fcopr.full_name))
251
252 output = {"output": "ok", "message": msg}
253 db.session.commit()
254
255 except (exceptions.ActionInProgressException,
256 exceptions.InsufficientRightsException) as err:
257 db.session.rollback()
258 raise LegacyApiError(str(err))
259 else:
260 raise LegacyApiError("Invalid request: {0}".format(form.errors))
261
262 return flask.jsonify(output)
263
264
265 @api_ns.route("/coprs/")
266 @api_ns.route("/coprs/<username>/")
267 -def api_coprs_by_owner(username=None):
268 """ Return the list of coprs owned by the given user.
269 username is taken either from GET params or from the URL itself
270 (in this order).
271
272 :arg username: the username of the person one would like to the
273 coprs of.
274
275 """
276 username = flask.request.args.get("username", None) or username
277 if username is None:
278 raise LegacyApiError("Invalid request: missing `username` ")
279
280 release_tmpl = "{chroot.os_release}-{chroot.os_version}-{chroot.arch}"
281
282 if username.startswith("@"):
283 group_name = username[1:]
284 query = CoprsLogic.get_multiple()
285 query = CoprsLogic.filter_by_group_name(query, group_name)
286 else:
287 query = CoprsLogic.get_multiple_owned_by_username(username)
288
289 query = CoprsLogic.join_builds(query)
290 query = CoprsLogic.set_query_order(query)
291
292 repos = query.all()
293 output = {"output": "ok", "repos": []}
294 for repo in repos:
295 yum_repos = {}
296 for build in repo.builds:
297 for chroot in repo.active_chroots:
298 release = release_tmpl.format(chroot=chroot)
299 yum_repos[release] = fix_protocol_for_backend(
300 os.path.join(build.copr.repo_url, release + '/'))
301 break
302
303 output["repos"].append({"name": repo.name,
304 "additional_repos": repo.repos,
305 "yum_repos": yum_repos,
306 "description": repo.description,
307 "instructions": repo.instructions,
308 "persistent": repo.persistent,
309 "unlisted_on_hp": repo.unlisted_on_hp,
310 "auto_prune": repo.auto_prune,
311 })
312
313 return flask.jsonify(output)
314
319 """ Return detail of one project.
320
321 :arg username: the username of the person one would like to the
322 coprs of.
323 :arg coprname: the name of project.
324
325 """
326 release_tmpl = "{chroot.os_release}-{chroot.os_version}-{chroot.arch}"
327 output = {"output": "ok", "detail": {}}
328 yum_repos = {}
329
330 build = models.Build.query.filter(models.Build.copr_id == copr.id).first()
331
332 if build:
333 for chroot in copr.active_chroots:
334 release = release_tmpl.format(chroot=chroot)
335 yum_repos[release] = fix_protocol_for_backend(
336 os.path.join(build.copr.repo_url, release + '/'))
337
338 output["detail"] = {
339 "name": copr.name,
340 "additional_repos": copr.repos,
341 "yum_repos": yum_repos,
342 "description": copr.description,
343 "instructions": copr.instructions,
344 "last_modified": builds_logic.BuildsLogic.last_modified(copr),
345 "auto_createrepo": copr.auto_createrepo,
346 "persistent": copr.persistent,
347 "unlisted_on_hp": copr.unlisted_on_hp,
348 "auto_prune": copr.auto_prune,
349 "use_bootstrap_container": copr.use_bootstrap_container,
350 }
351 return flask.jsonify(output)
352
353
354 @api_ns.route("/auth_check/", methods=["POST"])
355 @api_login_required
356 -def api_auth_check():
357 output = {"output": "ok"}
358 return flask.jsonify(output)
359
360
361 @api_ns.route("/coprs/<username>/<coprname>/new_webhook_secret/", methods=["POST"])
362 @api_login_required
363 @api_req_with_copr
364 -def new_webhook_secret(copr):
377
378
379 @api_ns.route("/coprs/<username>/<coprname>/new_build/", methods=["POST"])
380 @api_login_required
381 @api_req_with_copr
382 -def copr_new_build(copr):
394 return process_creating_new_build(copr, form, create_new_build)
395
396
397 @api_ns.route("/coprs/<username>/<coprname>/new_build_upload/", methods=["POST"])
398 @api_login_required
399 @api_req_with_copr
400 -def copr_new_build_upload(copr):
411 return process_creating_new_build(copr, form, create_new_build)
412
413
414 @api_ns.route("/coprs/<username>/<coprname>/new_build_pypi/", methods=["POST"])
415 @api_login_required
416 @api_req_with_copr
417 -def copr_new_build_pypi(copr):
434 return process_creating_new_build(copr, form, create_new_build)
435
436
437 @api_ns.route("/coprs/<username>/<coprname>/new_build_tito/", methods=["POST"])
438 @api_login_required
439 @api_req_with_copr
440 -def copr_new_build_tito(copr):
458 return process_creating_new_build(copr, form, create_new_build)
459
460
461 @api_ns.route("/coprs/<username>/<coprname>/new_build_mock/", methods=["POST"])
462 @api_login_required
463 @api_req_with_copr
464 -def copr_new_build_mock(copr):
482 return process_creating_new_build(copr, form, create_new_build)
483
484
485 @api_ns.route("/coprs/<username>/<coprname>/new_build_rubygems/", methods=["POST"])
486 @api_login_required
487 @api_req_with_copr
488 -def copr_new_build_rubygems(copr):
499 return process_creating_new_build(copr, form, create_new_build)
500
501
502 @api_ns.route("/coprs/<username>/<coprname>/new_build_custom/", methods=["POST"])
503 @api_login_required
504 @api_req_with_copr
505 -def copr_new_build_custom(copr):
518 return process_creating_new_build(copr, form, create_new_build)
519
520
521 @api_ns.route("/coprs/<username>/<coprname>/new_build_scm/", methods=["POST"])
522 @api_login_required
523 @api_req_with_copr
524 -def copr_new_build_scm(copr):
525 form = forms.BuildFormScmFactory(copr.active_chroots)(csrf_enabled=False)
526
527 def create_new_build():
528 return BuildsLogic.create_new_from_scm(
529 flask.g.user,
530 copr,
531 scm_type=form.scm_type.data,
532 clone_url=form.clone_url.data,
533 committish=form.committish.data,
534 subdirectory=form.subdirectory.data,
535 spec=form.spec.data,
536 srpm_build_method=form.srpm_build_method.data,
537 chroot_names=form.selected_chroots,
538 background=form.background.data,
539 )
540 return process_creating_new_build(copr, form, create_new_build)
541
542
543 @api_ns.route("/coprs/<username>/<coprname>/new_build_distgit/", methods=["POST"])
544 @api_login_required
545 @api_req_with_copr
546 -def copr_new_build_distgit(copr):
562 return process_creating_new_build(copr, form, create_new_build)
563
566 infos = []
567
568
569 infos.extend(validate_post_keys(form))
570
571 if not form.validate_on_submit():
572 raise LegacyApiError("Invalid request: bad request parameters: {0}".format(form.errors))
573
574 if not flask.g.user.can_build_in(copr):
575 raise LegacyApiError("Invalid request: user {} is not allowed to build in the copr: {}"
576 .format(flask.g.user.username, copr.full_name))
577
578
579 try:
580
581
582 build = create_new_build()
583 db.session.commit()
584 ids = [build.id] if type(build) != list else [b.id for b in build]
585 infos.append("Build was added to {0}:".format(copr.name))
586 for build_id in ids:
587 infos.append(" " + flask.url_for("coprs_ns.copr_build_redirect",
588 build_id=build_id,
589 _external=True))
590
591 except (ActionInProgressException, InsufficientRightsException) as e:
592 raise LegacyApiError("Invalid request: {}".format(e))
593
594 output = {"output": "ok",
595 "ids": ids,
596 "message": "\n".join(infos)}
597
598 return flask.jsonify(output)
599
600
601 @api_ns.route("/coprs/build_status/<int:build_id>/", methods=["GET"])
602 -def build_status(build_id):
607
608
609 @api_ns.route("/coprs/build_detail/<int:build_id>/", methods=["GET"])
610 @api_ns.route("/coprs/build/<int:build_id>/", methods=["GET"])
611 -def build_detail(build_id):
612 build = ComplexLogic.get_build_safe(build_id)
613
614 chroots = {}
615 results_by_chroot = {}
616 for chroot in build.build_chroots:
617 chroots[chroot.name] = chroot.state
618 results_by_chroot[chroot.name] = chroot.result_dir_url
619
620 built_packages = None
621 if build.built_packages:
622 built_packages = build.built_packages.split("\n")
623
624 output = {
625 "output": "ok",
626 "status": build.state,
627 "project": build.copr_name,
628 "project_dirname": build.copr_dirname,
629 "owner": build.copr.owner_name,
630 "results": build.copr.repo_url,
631 "built_pkgs": built_packages,
632 "src_version": build.pkg_version,
633 "chroots": chroots,
634 "submitted_on": build.submitted_on,
635 "started_on": build.min_started_on,
636 "ended_on": build.max_ended_on,
637 "src_pkg": build.pkgs,
638 "submitted_by": build.user.name if build.user else None,
639 "results_by_chroot": results_by_chroot
640 }
641 return flask.jsonify(output)
642
643
644 @api_ns.route("/coprs/cancel_build/<int:build_id>/", methods=["POST"])
645 @api_login_required
646 -def cancel_build(build_id):
657
658
659 @api_ns.route("/coprs/delete_build/<int:build_id>/", methods=["POST"])
660 @api_login_required
661 -def delete_build(build_id):
672
673
674 @api_ns.route('/coprs/<username>/<coprname>/modify/', methods=["POST"])
675 @api_login_required
676 @api_req_with_copr
677 -def copr_modify(copr):
678 form = forms.CoprModifyForm(csrf_enabled=False)
679
680 if not form.validate_on_submit():
681 raise LegacyApiError("Invalid request: {0}".format(form.errors))
682
683
684
685 if form.description.raw_data and len(form.description.raw_data):
686 copr.description = form.description.data
687 if form.instructions.raw_data and len(form.instructions.raw_data):
688 copr.instructions = form.instructions.data
689 if form.repos.raw_data and len(form.repos.raw_data):
690 copr.repos = form.repos.data
691 if form.disable_createrepo.raw_data and len(form.disable_createrepo.raw_data):
692 copr.disable_createrepo = form.disable_createrepo.data
693
694 if "unlisted_on_hp" in flask.request.form:
695 copr.unlisted_on_hp = form.unlisted_on_hp.data
696 if "build_enable_net" in flask.request.form:
697 copr.build_enable_net = form.build_enable_net.data
698 if "auto_prune" in flask.request.form:
699 copr.auto_prune = form.auto_prune.data
700 if "use_bootstrap_container" in flask.request.form:
701 copr.use_bootstrap_container = form.use_bootstrap_container.data
702 if "chroots" in flask.request.form:
703 coprs_logic.CoprChrootsLogic.update_from_names(
704 flask.g.user, copr, form.chroots.data)
705
706 try:
707 CoprsLogic.update(flask.g.user, copr)
708 if copr.group:
709 _ = copr.group.id
710 db.session.commit()
711 except (exceptions.ActionInProgressException,
712 exceptions.InsufficientRightsException,
713 exceptions.NonAdminCannotDisableAutoPrunning) as e:
714 db.session.rollback()
715 raise LegacyApiError("Invalid request: {}".format(e))
716
717 output = {
718 'output': 'ok',
719 'description': copr.description,
720 'instructions': copr.instructions,
721 'repos': copr.repos,
722 'chroots': [c.name for c in copr.mock_chroots],
723 }
724
725 return flask.jsonify(output)
726
727
728 @api_ns.route('/coprs/<username>/<coprname>/modify/<chrootname>/', methods=["POST"])
729 @api_login_required
730 @api_req_with_copr
731 -def copr_modify_chroot(copr, chrootname):
745
746
747 @api_ns.route('/coprs/<username>/<coprname>/chroot/edit/<chrootname>/', methods=["POST"])
748 @api_login_required
749 @api_req_with_copr
750 -def copr_edit_chroot(copr, chrootname):
751 form = forms.ModifyChrootForm(csrf_enabled=False)
752 chroot = ComplexLogic.get_copr_chroot_safe(copr, chrootname)
753
754 if not form.validate_on_submit():
755 raise LegacyApiError("Invalid request: {0}".format(form.errors))
756 else:
757 buildroot_pkgs = repos = comps_xml = comps_name = None
758 if "buildroot_pkgs" in flask.request.form:
759 buildroot_pkgs = form.buildroot_pkgs.data
760 if "repos" in flask.request.form:
761 repos = form.repos.data
762 if form.upload_comps.has_file():
763 comps_xml = form.upload_comps.data.stream.read()
764 comps_name = form.upload_comps.data.filename
765 if form.delete_comps.data:
766 coprs_logic.CoprChrootsLogic.remove_comps(flask.g.user, chroot)
767 coprs_logic.CoprChrootsLogic.update_chroot(
768 flask.g.user, chroot, buildroot_pkgs, repos, comps=comps_xml, comps_name=comps_name)
769 db.session.commit()
770
771 output = {
772 "output": "ok",
773 "message": "Edit chroot operation was successful.",
774 "chroot": chroot.to_dict(),
775 }
776 return flask.jsonify(output)
777
778
779 @api_ns.route('/coprs/<username>/<coprname>/detail/<chrootname>/', methods=["GET"])
780 @api_req_with_copr
781 -def copr_chroot_details(copr, chrootname):
786
787 @api_ns.route('/coprs/<username>/<coprname>/chroot/get/<chrootname>/', methods=["GET"])
788 @api_req_with_copr
789 -def copr_get_chroot(copr, chrootname):
793
797 """ Return the list of coprs found in search by the given text.
798 project is taken either from GET params or from the URL itself
799 (in this order).
800
801 :arg project: the text one would like find for coprs.
802
803 """
804 project = flask.request.args.get("project", None) or project
805 if not project:
806 raise LegacyApiError("No project found.")
807
808 try:
809 query = CoprsLogic.get_multiple_fulltext(project)
810
811 repos = query.all()
812 output = {"output": "ok", "repos": []}
813 for repo in repos:
814 output["repos"].append({"username": repo.user.name,
815 "coprname": repo.name,
816 "description": repo.description})
817 except ValueError as e:
818 raise LegacyApiError("Server error: {}".format(e))
819
820 return flask.jsonify(output)
821
825 """ Return list of coprs which are part of playground """
826 query = CoprsLogic.get_playground()
827 repos = query.all()
828 output = {"output": "ok", "repos": []}
829 for repo in repos:
830 output["repos"].append({"username": repo.owner_name,
831 "coprname": repo.name,
832 "chroots": [chroot.name for chroot in repo.active_chroots]})
833
834 jsonout = flask.jsonify(output)
835 jsonout.status_code = 200
836 return jsonout
837
838
839 @api_ns.route("/coprs/<username>/<coprname>/monitor/", methods=["GET"])
840 @api_req_with_copr
841 -def monitor(copr):
845
846
847
848 @api_ns.route("/coprs/<username>/<coprname>/package/add/<source_type_text>/", methods=["POST"])
849 @api_login_required
850 @api_req_with_copr
851 -def copr_add_package(copr, source_type_text):
853
854
855 @api_ns.route("/coprs/<username>/<coprname>/package/<package_name>/edit/<source_type_text>/", methods=["POST"])
856 @api_login_required
857 @api_req_with_copr
858 -def copr_edit_package(copr, package_name, source_type_text):
865
902
905 params = {}
906 if flask.request.args.get('with_latest_build'):
907 params['with_latest_build'] = True
908 if flask.request.args.get('with_latest_succeeded_build'):
909 params['with_latest_succeeded_build'] = True
910 if flask.request.args.get('with_all_builds'):
911 params['with_all_builds'] = True
912 return params
913
916 """
917 A lagging generator to stream JSON so we don't have to hold everything in memory
918 This is a little tricky, as we need to omit the last comma to make valid JSON,
919 thus we use a lagging generator, similar to http://stackoverflow.com/questions/1630320/
920 """
921 packages = query.__iter__()
922 try:
923 prev_package = next(packages)
924 except StopIteration:
925
926 yield '{"packages": []}'
927 raise StopIteration
928
929 yield '{"packages": ['
930
931 for package in packages:
932 yield json.dumps(prev_package.to_dict(**params)) + ', '
933 prev_package = package
934
935 yield json.dumps(prev_package.to_dict(**params)) + ']}'
936
937
938 @api_ns.route("/coprs/<username>/<coprname>/package/list/", methods=["GET"])
939 @api_req_with_copr
940 -def copr_list_packages(copr):
944
945
946
947 @api_ns.route("/coprs/<username>/<coprname>/package/get/<package_name>/", methods=["GET"])
948 @api_req_with_copr
949 -def copr_get_package(copr, package_name):
958
959
960 @api_ns.route("/coprs/<username>/<coprname>/package/delete/<package_name>/", methods=["POST"])
961 @api_login_required
962 @api_req_with_copr
963 -def copr_delete_package(copr, package_name):
980
981
982 @api_ns.route("/coprs/<username>/<coprname>/package/reset/<package_name>/", methods=["POST"])
983 @api_login_required
984 @api_req_with_copr
985 -def copr_reset_package(copr, package_name):
1002
1003
1004 @api_ns.route("/coprs/<username>/<coprname>/package/build/<package_name>/", methods=["POST"])
1005 @api_login_required
1006 @api_req_with_copr
1007 -def copr_build_package(copr, package_name):
1008 form = forms.BuildFormRebuildFactory.create_form_cls(copr.active_chroots)(csrf_enabled=False)
1009
1010 try:
1011 package = PackagesLogic.get(copr.main_dir.id, package_name)[0]
1012 except IndexError:
1013 raise LegacyApiError("No package with name {name} in copr {copr}".format(name=package_name, copr=copr.name))
1014
1015 if form.validate_on_submit():
1016 try:
1017 build = PackagesLogic.build_package(flask.g.user, copr, package, form.selected_chroots, **form.data)
1018 db.session.commit()
1019 except (InsufficientRightsException, ActionInProgressException, NoPackageSourceException) as e:
1020 raise LegacyApiError(str(e))
1021 else:
1022 raise LegacyApiError(form.errors)
1023
1024 return flask.jsonify({
1025 "output": "ok",
1026 "ids": [build.id],
1027 "message": "Build was added to {0}.".format(copr.name)
1028 })
1029
1030
1031 @api_ns.route("/coprs/<username>/<coprname>/module/build/", methods=["POST"])
1032 @api_login_required
1033 @api_req_with_copr
1034 -def copr_build_module(copr):
1057
1058
1059 @api_ns.route("/coprs/<username>/<coprname>/build-config/<chroot>/", methods=["GET"])
1060 @api_ns.route("/g/<group_name>/<coprname>/build-config/<chroot>/", methods=["GET"])
1061 @api_req_with_copr
1062 -def copr_build_config(copr, chroot):
1063 """
1064 Generate build configuration.
1065 """
1066 output = {
1067 "output": "ok",
1068 "build_config": generate_build_config(copr, chroot),
1069 }
1070
1071 if not output['build_config']:
1072 raise LegacyApiError('Chroot not found.')
1073
1074 return flask.jsonify(output)
1075