class Commands(object): def __init__(self, config): self.config = config try: self.client = CoprClient.create_from_file_config(config) except (copr_exceptions.CoprNoConfException, copr_exceptions.CoprConfigException): print(no_config_warning.format(config or "~/.config/copr")) self.client = CoprClient( copr_url=u"http://copr.fedoraproject.org", no_config=True ) def requires_api_auth(func): """ Decorator that checks config presence """ def wrapper(self, args): if self.client.no_config: print("Error: Operation requires api authentication") print(no_config_warning.format(self.config or "~/.config/copr")) sys.exit(6) return func(self, args) wrapper.__doc__ = func.__doc__ wrapper.__name__ = func.__name__ return wrapper def check_username_presence(func): """ Decorator that checks if username was provided """ def wrapper(self, args): if self.client.no_config and args.username is None: print("Error: Operation requires username\n" "Pass username to command or create `~/.config/copr`") sys.exit(6) if args.username is None and self.client.username is None: print("Error: Operation requires username\n" "Pass username to command or add it to `~/.config/copr`") sys.exit(6) return func(self, args) wrapper.__doc__ = func.__doc__ wrapper.__name__ = func.__name__ return wrapper def _watch_builds(self, build_ids): """ :param build_ids: list of build IDs """ print("Watching build(s): (this may be safely interrupted)") prevstatus = defaultdict(lambda: None) failed_ids = [] watched = set(build_ids) done = set() try: while watched != done: for build_id in watched: if build_id in done: continue build_details = self.client.get_build_details(build_id) if build_details.output != "ok": errmsg = " Build {1}: Unable to get build status: {0}". \ format(build_details.error, build_id) raise copr_exceptions.CoprRequestException(errmsg) now = datetime.datetime.now() if prevstatus[build_id] != build_details.status: prevstatus[build_id] = build_details.status print(" {0} Build {2}: {1}".format( now.strftime("%H:%M:%S"), build_details.status, build_id)) if build_details.status in ["failed"]: failed_ids.append(build_id) if build_details.status in ["succeeded", "skipped", "failed", "canceled"]: done.add(build_id) if build_details.status == "unknown": raise copr_exceptions.CoprBuildException( "Unknown status.") if watched == done: break time.sleep(30) if failed_ids: raise copr_exceptions.CoprBuildException( "Build(s) {0} failed.".format( ", ".join(str(x) for x in failed_ids))) except KeyboardInterrupt: pass @requires_api_auth def action_build(self, args): """ Method called when the 'build' action has been selected by the user. :param args: argparse arguments provided by the user """ bar = None progress_callback = None if os.path.exists(args.pkgs[0]): bar = ProgressBar(maxval=os.path.getsize(args.pkgs[0])) # pylint: disable=function-redefined def progress_callback(monitor): bar.next(n=8192) print('Uploading package {0}'.format(args.pkgs[0])) data = { "pkgs": args.pkgs, "progress_callback": progress_callback, } return self.process_build(args, self.client.create_new_build, data, bar=bar) @requires_api_auth def action_build_pypi(self, args): """ Method called when the 'buildpypi' action has been selected by the user. :param args: argparse arguments provided by the user """ username, copr = parse_name(args.copr) data = { "pypi_package_name": args.packagename, "pypi_package_version": args.packageversion, "python_versions": args.pythonversions, } return self.process_build(args, self.client.create_new_build_pypi, data) @requires_api_auth def action_build_tito(self, args): """ Method called when the 'buildtito' action has been selected by the user. :param args: argparse arguments provided by the user """ data = { "git_url": args.git_url, "git_dir": args.git_dir, "git_branch": args.git_branch, "tito_test": args.tito_test, } return self.process_build(args, self.client.create_new_build_tito, data) @requires_api_auth def action_build_mock(self, args): """ Method called when the 'build-mock' action has been selected by the user. :param args: argparse arguments provided by the user """ data = { "scm_type": args.scm_type, "scm_url": args.scm_url, "scm_branch": args.scm_branch, "spec": args.spec, } return self.process_build(args, self.client.create_new_build_mock, data) @requires_api_auth def action_build_rubygems(self, args): """ Method called when the 'buildgem' action has been selected by the user. :param args: argparse arguments provided by the user """ data = {"gem_name": args.gem_name} return self.process_build(args, self.client.create_new_build_rubygems, data) def process_build(self, args, build_function, data, bar=None): username, copr = parse_name(args.copr) result = build_function(username=username, projectname=copr, chroots=args.chroots, memory=args.memory, timeout=args.timeout, background=args.background, **data) if bar: bar.finish() if result.output != "ok": print(result.error) return print(result.message) build_ids = [bw.build_id for bw in result.builds_list] print("Created builds: {0}".format(" ".join(map(str, build_ids)))) if not args.nowait: self._watch_builds(build_ids) @requires_api_auth def action_create(self, args): """ Method called when the 'create' action has been selected by the user. :param args: argparse arguments provided by the user """ username, copr = parse_name(args.name) result = self.client.create_project( username=username, projectname=copr, description=args.description, instructions=args.instructions, chroots=args.chroots, repos=args.repos, initial_pkgs=args.initial_pkgs, disable_createrepo=args.disable_createrepo, unlisted_on_hp=(args.unlisted_on_hp == 'on'), enable_net=(args.enable_net == 'on') ) print(result.message) @requires_api_auth def action_modify_project(self, args): """ Method called when the 'modify' action has been selected by the user. :param args: argparse arguments provided by the user """ username, copr = parse_name(args.name) result = self.client.modify_project( username=username, projectname=copr, description=args.description, instructions=args.instructions, repos=args.repos, disable_createrepo=args.disable_createrepo, unlisted_on_hp=(args.unlisted_on_hp == 'on' if args.unlisted_on_hp else None), enable_net=(args.enable_net == 'on' if args.enable_net else None) ) @requires_api_auth def action_delete(self, args): """ Method called when the 'delete' action has been selected by the user. :param args: argparse arguments provided by the user """ username, copr = parse_name(args.copr) result = self.client.delete_project(username=username, projectname=copr) print(result.message) @requires_api_auth def action_fork(self, args): """ Method called when the 'fork' action has been selected by the user. :param args: argparse arguments provided by the user """ username, copr = parse_name(args.dst) result = self.client.fork_project(source=args.src, username=username, projectname=copr, confirm=args.confirm) print(result.message) @check_username_presence def action_list(self, args): """ Method called when the 'list' action has been selected by the user. :param args: argparse arguments provided by the user """ username = args.username or self.client.username result = self.client.get_projects_list(username) # import ipdb; ipdb.set_trace() if result.output != "ok": print(result.error) print("Un-expected data returned, please report this issue") elif not result.projects_list: print("No copr retrieved for user: '******'".format(username)) return for prj in result.projects_list: print(prj) def action_status(self, args): result = self.client.get_build_details(args.build_id) print(result.status) def action_download_build(self, args): result = self.client.get_build_details(args.build_id) base_len = len(os.path.split(result.results)) for chroot, url in result.results_by_chroot.items(): if args.chroots and chroot not in args.chroots: continue cmd = "wget -r -nH --no-parent --reject 'index.html*'".split(' ') cmd.extend(['-P', os.path.join(args.dest, chroot)]) cmd.extend(['--cut-dirs', str(base_len + 4)]) cmd.append(url) subprocess.call(cmd) @requires_api_auth def action_cancel(self, args): """ Method called when the 'cancel' action has been selected by the user. :param args: argparse arguments provided by the user """ result = self.client.cancel_build(args.build_id) print(result.status) def action_watch_build(self, args): self._watch_builds(args.build_id) ######################################################### ### Package actions ### ######################################################### @requires_api_auth def action_add_or_edit_package_tito(self, args): ownername, projectname = parse_name(args.copr) data = { "package_name": args.name, "git_url": args.git_url, "git_dir": args.git_dir, "git_branch": args.git_branch, "tito_test": args.tito_test == 'on', "webhook_rebuild": args.webhook_rebuild == 'on', } if args.create: result = self.client.add_package_tito(ownername=ownername, projectname=projectname, **data) else: result = self.client.edit_package_tito(ownername=ownername, projectname=projectname, **data) print(result.message) @requires_api_auth def action_add_or_edit_package_pypi(self, args): ownername, projectname = parse_name(args.copr) data = { "package_name": args.name, "pypi_package_name": args.packagename, "pypi_package_version": args.packageversion, "python_versions": args.pythonversions, "webhook_rebuild": args.webhook_rebuild == 'on', } if args.create: result = self.client.add_package_pypi(ownername=ownername, projectname=projectname, **data) else: result = self.client.edit_package_pypi(ownername=ownername, projectname=projectname, **data) print(result.message) @requires_api_auth def action_add_or_edit_package_mockscm(self, args): ownername, projectname = parse_name(args.copr) data = { "package_name": args.name, "scm_type": args.scm_type, "scm_url": args.scm_url, "scm_branch": args.scm_branch, "spec": args.spec, "webhook_rebuild": args.webhook_rebuild == 'on', } if args.create: result = self.client.add_package_mockscm(ownername=ownername, projectname=projectname, **data) else: result = self.client.edit_package_mockscm(ownername=ownername, projectname=projectname, **data) print(result.message) @requires_api_auth def action_add_or_edit_package_rubygems(self, args): ownername, projectname = parse_name(args.copr) data = { "package_name": args.name, "gem_name": args.gem_name, "webhook_rebuild": args.webhook_rebuild == 'on', } if args.create: result = self.client.add_package_rubygems(ownername=ownername, projectname=projectname, **data) else: result = self.client.edit_package_rubygems(ownername=ownername, projectname=projectname, **data) print(result.message) def action_list_packages(self, args): ownername, projectname = parse_name(args.copr) data = { "with_latest_build": args.with_latest_build, "with_latest_succeeded_build": args.with_latest_succeeded_build, "with_all_builds": args.with_all_builds, } result = self.client.get_packages_list(ownername=ownername, projectname=projectname, **data) print(simplejson.dumps(result.packages_list, indent=4, sort_keys=True, for_json=True)) def action_list_package_names(self, args): ownername, projectname = parse_name(args.copr) result = self.client.get_packages_list(ownername=ownername, projectname=projectname) for package in result.packages_list: print(package.name) def action_get_package(self, args): ownername, projectname = parse_name(args.copr) data = { "pkg_name": args.name, "with_latest_build": args.with_latest_build, "with_latest_succeeded_build": args.with_latest_succeeded_build, "with_all_builds": args.with_all_builds, } result = self.client.get_package(ownername=ownername, projectname=projectname, **data) print(simplejson.dumps(result.package, indent=4, sort_keys=True, for_json=True)) def action_delete_package(self, args): ownername, projectname = parse_name(args.copr) data = { "pkg_name": args.name } result = self.client.delete_package(ownername=ownername, projectname=projectname, **data) print(result.message) def action_reset_package(self, args): ownername, projectname = parse_name(args.copr) data = { "pkg_name": args.name } result = self.client.reset_package(ownername=ownername, projectname=projectname, **data) print(result.message) def action_build_package(self, args): ownername, projectname = parse_name(args.copr) data = { "pkg_name": args.name, "chroots": args.chroots, #"memory": args.memory, "timeout": args.timeout } result = self.client.build_package(ownername=ownername, projectname=projectname, **data) if result.output != "ok": print(result.error) return print(result.message) build_ids = [bw.build_id for bw in result.builds_list] print("Created builds: {0}".format(" ".join(map(str, build_ids)))) if not args.nowait: self._watch_builds(build_ids)
class Commands(object): def __init__(self, config): self.config = config try: self.client = CoprClient.create_from_file_config(config) except (copr_exceptions.CoprNoConfException, copr_exceptions.CoprConfigException): sys.stderr.write(no_config_warning.format(config or "~/.config/copr")) self.client = CoprClient( copr_url=u"http://copr.fedoraproject.org", no_config=True ) def requires_api_auth(func): """ Decorator that checks config presence """ def wrapper(self, args): if self.client.no_config: sys.stderr.write("Error: Operation requires api authentication\n") sys.exit(6) return func(self, args) wrapper.__doc__ = func.__doc__ wrapper.__name__ = func.__name__ return wrapper def check_username_presence(func): """ Decorator that checks if username was provided """ def wrapper(self, args): if self.client.no_config and args.username is None: sys.stderr.write( "Error: Operation requires username\n" "Pass username to command or create `~/.config/copr`\n") sys.exit(6) if args.username is None and self.client.username is None: sys.stderr.write( "Error: Operation requires username\n" "Pass username to command or add it to `~/.config/copr`\n") sys.exit(6) return func(self, args) wrapper.__doc__ = func.__doc__ wrapper.__name__ = func.__name__ return wrapper def _watch_builds(self, build_ids): """ :param build_ids: list of build IDs """ print("Watching build(s): (this may be safely interrupted)") prevstatus = defaultdict(lambda: None) failed_ids = [] watched = set(build_ids) done = set() try: while watched != done: for build_id in watched: if build_id in done: continue build_details = self.client.get_build_details(build_id) if build_details.output != "ok": errmsg = " Build {1}: Unable to get build status: {0}". \ format(build_details.error, build_id) raise copr_exceptions.CoprRequestException(errmsg) now = datetime.datetime.now() if prevstatus[build_id] != build_details.status: prevstatus[build_id] = build_details.status print(" {0} Build {2}: {1}".format( now.strftime("%H:%M:%S"), build_details.status, build_id)) if build_details.status in ["failed"]: failed_ids.append(build_id) if build_details.status in ["succeeded", "skipped", "failed", "canceled"]: done.add(build_id) if build_details.status == "unknown": raise copr_exceptions.CoprBuildException( "Unknown status.") if watched == done: break time.sleep(30) if failed_ids: raise copr_exceptions.CoprBuildException( "Build(s) {0} failed.".format( ", ".join(str(x) for x in failed_ids))) except KeyboardInterrupt: pass def action_whoami(self, args): """ Simply print out the current user as defined in copr config. """ print(self.client.username) @requires_api_auth def action_build(self, args): """ Method called when the 'build' action has been selected by the user. :param args: argparse arguments provided by the user """ self.client.authentication_check() bar = None progress_callback = None builds = [] for pkg in args.pkgs: if os.path.exists(pkg): bar = ProgressBar(max=os.path.getsize(pkg)) # pylint: disable=function-redefined def progress_callback(monitor): bar.next(n=8192) print('Uploading package {0}'.format(pkg)) data = { "pkgs": [pkg], "progress_callback": progress_callback, } builds.append(self.process_build(args, self.client.create_new_build, data, bar=bar)) return builds @requires_api_auth def action_build_pypi(self, args): """ Method called when the 'buildpypi' action has been selected by the user. :param args: argparse arguments provided by the user """ username, copr = parse_name(args.copr) data = { "pypi_package_name": args.packagename, "pypi_package_version": args.packageversion, "python_versions": args.pythonversions, } return self.process_build(args, self.client.create_new_build_pypi, data) @requires_api_auth def action_build_tito(self, args): """ Method called when the 'buildtito' action has been selected by the user. :param args: argparse arguments provided by the user """ data = { "git_url": args.git_url, "git_dir": args.git_dir, "git_branch": args.git_branch, "tito_test": args.tito_test, } return self.process_build(args, self.client.create_new_build_tito, data) @requires_api_auth def action_build_mock(self, args): """ Method called when the 'build-mock' action has been selected by the user. :param args: argparse arguments provided by the user """ data = { "scm_type": args.scm_type, "scm_url": args.scm_url, "scm_branch": args.scm_branch, "spec": args.spec, } return self.process_build(args, self.client.create_new_build_mock, data) @requires_api_auth def action_build_rubygems(self, args): """ Method called when the 'buildgem' action has been selected by the user. :param args: argparse arguments provided by the user """ data = {"gem_name": args.gem_name} return self.process_build(args, self.client.create_new_build_rubygems, data) @requires_api_auth def action_build_distgit(self, args): """ Method called when the 'buildfedpkg' action has been selected by the user. :param args: argparse arguments provided by the user """ data = {"clone_url": args.clone_url, "branch": args.branch} return self.process_build(args, self.client.create_new_build_distgit, data) def process_build(self, args, build_function, data, bar=None): username, copr = parse_name(args.copr) result = build_function(username=username, projectname=copr, chroots=args.chroots, memory=args.memory, timeout=args.timeout, background=args.background, **data) if bar: bar.finish() if result.output != "ok": sys.stderr.write(result.error + "\n") return print(result.message) build_ids = [bw.build_id for bw in result.builds_list] print("Created builds: {0}".format(" ".join(map(str, build_ids)))) if not args.nowait: self._watch_builds(build_ids) @requires_api_auth def action_create(self, args): """ Method called when the 'create' action has been selected by the user. :param args: argparse arguments provided by the user """ username, copr = parse_name(args.name) result = self.client.create_project( username=username, projectname=copr, description=args.description, instructions=args.instructions, chroots=args.chroots, repos=args.repos, initial_pkgs=args.initial_pkgs, disable_createrepo=args.disable_createrepo, unlisted_on_hp=ON_OFF_MAP[args.unlisted_on_hp], enable_net=ON_OFF_MAP[args.enable_net], persistent=args.persistent, auto_prune=ON_OFF_MAP[args.auto_prune] ) print(result.message) @requires_api_auth def action_modify_project(self, args): """ Method called when the 'modify' action has been selected by the user. :param args: argparse arguments provided by the user """ username, copr = parse_name(args.name) result = self.client.modify_project( username=username, projectname=copr, description=args.description, instructions=args.instructions, repos=args.repos, disable_createrepo=args.disable_createrepo, unlisted_on_hp=ON_OFF_MAP[args.unlisted_on_hp], enable_net=ON_OFF_MAP[args.enable_net], auto_prune=ON_OFF_MAP[args.auto_prune], chroots=args.chroots, ) @requires_api_auth def action_delete(self, args): """ Method called when the 'delete' action has been selected by the user. :param args: argparse arguments provided by the user """ username, copr = parse_name(args.copr) result = self.client.delete_project(username=username, projectname=copr) print(result.message) @requires_api_auth def action_fork(self, args): """ Method called when the 'fork' action has been selected by the user. :param args: argparse arguments provided by the user """ username, copr = parse_name(args.dst) result = self.client.fork_project(source=args.src, username=username, projectname=copr, confirm=args.confirm) print(result.message) def action_mock_config(self, args): """ Method called when the 'list' action has been selected by the user. :param args: argparse arguments provided by the user """ username = self.client.username project = args.project.split("/") if len(project) != 2: args.project = username + "/" + args.project result = self.client.get_build_config(args.project, args.chroot) if result.output != "ok": sys.stderr.write(result.error + "\n") sys.stderr.write("Un-expected data returned, please report this issue\n") print(MockProfile(result.build_config)) @check_username_presence def action_list(self, args): """ Method called when the 'list' action has been selected by the user. :param args: argparse arguments provided by the user """ username = args.username or self.client.username result = self.client.get_projects_list(username) # import ipdb; ipdb.set_trace() if result.output != "ok": sys.stderr.write(result.error + "\n") sys.stderr.write("Un-expected data returned, please report this issue\n") elif not result.projects_list: sys.stderr.write("No copr retrieved for user: '******'\n".format(username)) return for prj in result.projects_list: print(prj) def action_status(self, args): result = self.client.get_build_details(args.build_id) print(result.status) def action_download_build(self, args): result = self.client.get_build_details(args.build_id) base_len = len(os.path.split(result.results)) for chroot, url in result.results_by_chroot.items(): if args.chroots and chroot not in args.chroots: continue cmd = "wget -r -nH --no-parent --reject 'index.html*'".split(' ') cmd.extend(['-P', os.path.join(args.dest, chroot)]) cmd.extend(['--cut-dirs', str(base_len + 4)]) cmd.append(url) subprocess.call(cmd) @requires_api_auth def action_cancel(self, args): """ Method called when the 'cancel' action has been selected by the user. :param args: argparse arguments provided by the user """ result = self.client.cancel_build(args.build_id) print(result.status) def action_watch_build(self, args): self._watch_builds(args.build_id) def action_delete_build(self, args): result = self.client.delete_build(args.build_id) print(result.status) ######################################################### ### Chroot actions ### ######################################################### @requires_api_auth def action_edit_chroot(self, args): """ Method called when the 'edit-chroot' action has been selected by the user. :param args: argparse arguments provided by the user """ owner, copr, chroot = parse_chroot_path(args.coprchroot) result = self.client.edit_chroot( ownername=owner, projectname=copr, chrootname=chroot, upload_comps=args.upload_comps, delete_comps=args.delete_comps, packages=args.packages, repos=args.repos ) print(result.message) def action_get_chroot(self, args): """ Method called when the 'get-chroot' action has been selected by the user. :param args: argparse arguments provided by the user """ owner, copr, chroot = parse_chroot_path(args.coprchroot) result = self.client.get_chroot( ownername=owner, projectname=copr, chrootname=chroot ) print(simplejson.dumps(result.chroot, indent=4, sort_keys=True, for_json=True)) ######################################################### ### Package actions ### ######################################################### @requires_api_auth def action_add_or_edit_package_tito(self, args): ownername, projectname = parse_name(args.copr) data = { "package_name": args.name, "git_url": args.git_url, "git_dir": args.git_dir, "git_branch": args.git_branch, "tito_test": ON_OFF_MAP[args.tito_test], "webhook_rebuild": ON_OFF_MAP[args.webhook_rebuild], } if args.create: result = self.client.add_package_tito(ownername=ownername, projectname=projectname, **data) else: result = self.client.edit_package_tito(ownername=ownername, projectname=projectname, **data) print(result.message) @requires_api_auth def action_add_or_edit_package_pypi(self, args): ownername, projectname = parse_name(args.copr) data = { "package_name": args.name, "pypi_package_name": args.packagename, "pypi_package_version": args.packageversion, "python_versions": args.pythonversions, "webhook_rebuild": ON_OFF_MAP[args.webhook_rebuild], } if args.create: result = self.client.add_package_pypi(ownername=ownername, projectname=projectname, **data) else: result = self.client.edit_package_pypi(ownername=ownername, projectname=projectname, **data) print(result.message) @requires_api_auth def action_add_or_edit_package_mockscm(self, args): ownername, projectname = parse_name(args.copr) data = { "package_name": args.name, "scm_type": args.scm_type, "scm_url": args.scm_url, "scm_branch": args.scm_branch, "spec": args.spec, "webhook_rebuild": ON_OFF_MAP[args.webhook_rebuild], } if args.create: result = self.client.add_package_mockscm(ownername=ownername, projectname=projectname, **data) else: result = self.client.edit_package_mockscm(ownername=ownername, projectname=projectname, **data) print(result.message) @requires_api_auth def action_add_or_edit_package_rubygems(self, args): ownername, projectname = parse_name(args.copr) data = { "package_name": args.name, "gem_name": args.gem_name, "webhook_rebuild": ON_OFF_MAP[args.webhook_rebuild], } if args.create: result = self.client.add_package_rubygems(ownername=ownername, projectname=projectname, **data) else: result = self.client.edit_package_rubygems(ownername=ownername, projectname=projectname, **data) print(result.message) def action_list_packages(self, args): ownername, projectname = parse_name(args.copr) data = { "with_latest_build": args.with_latest_build, "with_latest_succeeded_build": args.with_latest_succeeded_build, "with_all_builds": args.with_all_builds, } result = self.client.get_packages_list(ownername=ownername, projectname=projectname, **data) print(simplejson.dumps(result.packages_list, indent=4, sort_keys=True, for_json=True)) def action_list_package_names(self, args): ownername, projectname = parse_name(args.copr) result = self.client.get_packages_list(ownername=ownername, projectname=projectname) for package in result.packages_list: print(package.name) def action_get_package(self, args): ownername, projectname = parse_name(args.copr) data = { "pkg_name": args.name, "with_latest_build": args.with_latest_build, "with_latest_succeeded_build": args.with_latest_succeeded_build, "with_all_builds": args.with_all_builds, } result = self.client.get_package(ownername=ownername, projectname=projectname, **data) print(simplejson.dumps(result.package, indent=4, sort_keys=True, for_json=True)) def action_delete_package(self, args): ownername, projectname = parse_name(args.copr) data = { "pkg_name": args.name } result = self.client.delete_package(ownername=ownername, projectname=projectname, **data) print(result.message) def action_reset_package(self, args): ownername, projectname = parse_name(args.copr) data = { "pkg_name": args.name } result = self.client.reset_package(ownername=ownername, projectname=projectname, **data) print(result.message) def action_build_package(self, args): ownername, projectname = parse_name(args.copr) data = { "pkg_name": args.name, "chroots": args.chroots, #"memory": args.memory, "timeout": args.timeout } result = self.client.build_package(ownername=ownername, projectname=projectname, **data) if result.output != "ok": sys.stderr.write(result.error + "\n") return print(result.message) build_ids = [bw.build_id for bw in result.builds_list] print("Created builds: {0}".format(" ".join(map(str, build_ids)))) if not args.nowait: self._watch_builds(build_ids) def action_build_module(self, args): """ Build module via Copr MBS """ ownername, projectname = parse_name(args.copr or "") modulemd = open(args.yaml, "rb") if args.yaml else args.url response = self.client.build_module(modulemd, ownername, projectname) print(response.message if response.output == "ok" else response.error)