You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
473 lines
16 KiB
473 lines
16 KiB
diff -N -up -r a/docs/Makefile b/docs/Makefile |
|
--- a/docs/Makefile 2017-03-21 14:21:12.326518685 +0100 |
|
+++ b/docs/Makefile 2017-03-21 14:24:34.711624331 +0100 |
|
@@ -5,7 +5,7 @@ DOCS = repoquery package-cleanup repo-rs |
|
find-repos-of-install needs-restarting repo-graph repoclosure \ |
|
repomanage repotrack verifytree yum-config-manager yum-ovl |
|
DOCS5 = yum-changelog.conf yum-versionlock.conf yum-fs-snapshot.conf |
|
-DOCS8 = yum-complete-transaction yumdb |
|
+DOCS8 = yum-complete-transaction yumdb yum-copr |
|
|
|
all: |
|
echo "Nothing to do" |
|
diff -N -up -r a/docs/yum-copr.8 b/docs/yum-copr.8 |
|
--- a/docs/yum-copr.8 1970-01-01 01:00:00.000000000 +0100 |
|
+++ b/docs/yum-copr.8 2017-03-21 14:23:17.436729456 +0100 |
|
@@ -0,0 +1,120 @@ |
|
+.\" Man page generated from reStructuredText. |
|
+. |
|
+.TH "YUM-COPR" "8" "July 29, 2014" "0.1.1" "yum-plugin-copr" |
|
+.SH NAME |
|
+yum-plugin-copr \- YUM copr Plugin |
|
+. |
|
+.nr rst2man-indent-level 0 |
|
+. |
|
+.de1 rstReportMargin |
|
+\\$1 \\n[an-margin] |
|
+level \\n[rst2man-indent-level] |
|
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] |
|
+- |
|
+\\n[rst2man-indent0] |
|
+\\n[rst2man-indent1] |
|
+\\n[rst2man-indent2] |
|
+.. |
|
+.de1 INDENT |
|
+.\" .rstReportMargin pre: |
|
+. RS \\$1 |
|
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] |
|
+. nr rst2man-indent-level +1 |
|
+.\" .rstReportMargin post: |
|
+.. |
|
+.de UNINDENT |
|
+. RE |
|
+.\" indent \\n[an-margin] |
|
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] |
|
+.nr rst2man-indent-level -1 |
|
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] |
|
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u |
|
+.. |
|
+. |
|
+.nr rst2man-indent-level 0 |
|
+. |
|
+.de1 rstReportMargin |
|
+\\$1 \\n[an-margin] |
|
+level \\n[rst2man-indent-level] |
|
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] |
|
+- |
|
+\\n[rst2man-indent0] |
|
+\\n[rst2man-indent1] |
|
+\\n[rst2man-indent2] |
|
+.. |
|
+.de1 INDENT |
|
+.\" .rstReportMargin pre: |
|
+. RS \\$1 |
|
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] |
|
+. nr rst2man-indent-level +1 |
|
+.\" .rstReportMargin post: |
|
+.. |
|
+.de UNINDENT |
|
+. RE |
|
+.\" indent \\n[an-margin] |
|
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] |
|
+.nr rst2man-indent-level -1 |
|
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] |
|
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u |
|
+.. |
|
+.sp |
|
+Work with Copr & Playground repositories on the local system. |
|
+.INDENT 0.0 |
|
+.IP \(bu 2 |
|
+The \fBcopr\fP command is used to add or remove Copr repositories to the local system |
|
+.IP \(bu 2 |
|
+The \fBplayground\fP is used to enable or disable the Playground repository |
|
+.UNINDENT |
|
+.SH SYNOPSIS |
|
+.sp |
|
+\fByum copr [enable|disable|list|search] <parameters>\fP |
|
+.sp |
|
+\fByum playground [enable|disable|upgrade]\fP |
|
+.SH ARGUMENTS (COPR) |
|
+.INDENT 0.0 |
|
+.TP |
|
+.B \fBenable name/project [chroot]\fP |
|
+Enable the \fBname/project\fP Copr repository with the optional \fBchroot\fP\&. |
|
+.TP |
|
+.B \fBdisable name/project\fP |
|
+Disable the \fBname/project\fP Copr repository. |
|
+.TP |
|
+.B \fBlist name\fP |
|
+List available Copr repositories for a given \fBname\fP\&. |
|
+.TP |
|
+.B \fBsearch project\fP |
|
+Search for a given \fBproject\fP\&. |
|
+.UNINDENT |
|
+.SH ARGUMENTS (PLAYGROUND) |
|
+.INDENT 0.0 |
|
+.TP |
|
+.B \fBenable\fP |
|
+Enable the Playground repository. |
|
+.TP |
|
+.B \fBdisable\fP |
|
+Disable the Playground repository. |
|
+.TP |
|
+.B \fBupgrade\fP |
|
+Upgrade the Playground repository settings (same as \fBdisable\fP and then \fBenable\fP). |
|
+.UNINDENT |
|
+.SH EXAMPLES |
|
+.INDENT 0.0 |
|
+.TP |
|
+.B \fBcopr enable rhscl/perl516 epel\-6\-x86_64\fP |
|
+Enable the \fBrhscl/perl516\fP Copr repository, using the \fBepel\-6\-x86_64\fP chroot. |
|
+.TP |
|
+.B \fBcopr disable rhscl/perl516\fP |
|
+Disable the \fBrhscl/perl516\fP Copr repository |
|
+.TP |
|
+.B \fBcopr list rita\fP |
|
+List available Copr projects for user \fBrita\fP\&. |
|
+.TP |
|
+.B \fBcopr search tests\fP |
|
+Search for Copr projects named \fBtests\fP\&. |
|
+.UNINDENT |
|
+.SH AUTHOR |
|
+See AUTHORS in the Core DNF Plugins distribution |
|
+.SH COPYRIGHT |
|
+2014, Red Hat, Licensed under GPLv2+ |
|
+.\" Generated by docutils manpage writer. |
|
+. |
|
diff -N -up -r a/plugins/copr/copr.conf b/plugins/copr/copr.conf |
|
--- a/plugins/copr/copr.conf 1970-01-01 01:00:00.000000000 +0100 |
|
+++ b/plugins/copr/copr.conf 2017-03-21 14:22:49.094134790 +0100 |
|
@@ -0,0 +1,2 @@ |
|
+[main] |
|
+enabled=1 |
|
diff -N -up -r a/plugins/copr/copr.py b/plugins/copr/copr.py |
|
--- a/plugins/copr/copr.py 1970-01-01 01:00:00.000000000 +0100 |
|
+++ b/plugins/copr/copr.py 2017-03-21 14:22:49.095134776 +0100 |
|
@@ -0,0 +1,327 @@ |
|
+# supplies the 'copr' command. |
|
+# |
|
+# Copyright (C) 2014 Red Hat, Inc. |
|
+# |
|
+# This copyrighted material is made available to anyone wishing to use, |
|
+# modify, copy, or redistribute it subject to the terms and conditions of |
|
+# the GNU General Public License v.2, or (at your option) any later version. |
|
+# This program is distributed in the hope that it will be useful, but WITHOUT |
|
+# ANY WARRANTY expressed or implied, including the implied warranties of |
|
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
+# Public License for more details. You should have received a copy of the |
|
+# GNU General Public License along with this program; if not, write to the |
|
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
+# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the |
|
+# source code or documentation are not subject to the GNU General Public |
|
+# License and may only be used or replicated with the express permission of |
|
+# Red Hat, Inc. |
|
+# |
|
+ |
|
+"""YUM plugin supplying the 'copr' command.""" |
|
+ |
|
+from urlgrabber import grabber |
|
+ |
|
+import yum |
|
+import glob |
|
+import json |
|
+import os |
|
+import platform |
|
+import requests |
|
+import urllib |
|
+ |
|
+from yum.i18n import _ |
|
+from yum.plugins import TYPE_INTERACTIVE |
|
+ |
|
+requires_api_version = '2.5' |
|
+plugin_type = (TYPE_INTERACTIVE,) |
|
+ |
|
+yes = set([_('yes'), _('y')]) |
|
+no = set([_('no'), _('n'), '']) |
|
+ |
|
+YError = yum.Errors.YumBaseError |
|
+YCliError = yum.Errors.MiscError |
|
+ |
|
+def config_hook(conduit): |
|
+ conduit.registerCommand(CoprCommand()) |
|
+ conduit.registerCommand(PlaygroundCommand()) |
|
+ |
|
+class CoprCommand: |
|
+ """ Copr plugin for DNF """ |
|
+ |
|
+ def getNames(self): |
|
+ return [self.aliases[0]] |
|
+ |
|
+ def getUsage(self): |
|
+ return self.usage |
|
+ |
|
+ def getSummary(self): |
|
+ return self.summary[1:] |
|
+ |
|
+ def doCheck(self, base, basecmd, extcmds): |
|
+ self.base = base |
|
+ |
|
+ copr_url = "https://copr.fedoraproject.org" |
|
+ aliases = ("copr",) |
|
+ summary = _("Interact with Copr repositories.") |
|
+ usage = _(""" |
|
+ enable name/project [chroot] |
|
+ disable name/project |
|
+ list name |
|
+ search project |
|
+ |
|
+ Examples: |
|
+ copr enable rhscl/perl516 epel-6-x86_64 |
|
+ copr enable ignatenkobrain/ocltoys |
|
+ copr disable rhscl/perl516 |
|
+ copr list ignatenkobrain |
|
+ copr search tests |
|
+ """) |
|
+ |
|
+ def doCommand(self, base, basecmd, extcmds): |
|
+ try: |
|
+ subcommand = extcmds[0] |
|
+ project_name = extcmds[1] |
|
+ except (ValueError, IndexError): |
|
+ base.logger.critical( |
|
+ _('Error: ') + |
|
+ _('exactly two additional parameters to ' |
|
+ 'copr command are required')) |
|
+ # FIXME |
|
+ # dnf.cli.commands.err_mini_usage(self.cli, self.cli.base.basecmd) |
|
+ raise YCliError( |
|
+ _('exactly two additional parameters to ' |
|
+ 'copr command are required')) |
|
+ try: |
|
+ chroot = extcmds[2] |
|
+ except IndexError: |
|
+ chroot = self._guess_chroot() |
|
+ repo_filename = "/etc/yum.repos.d/_copr_{}.repo" \ |
|
+ .format(project_name.replace("/", "-")) |
|
+ if subcommand == "enable": |
|
+ self._need_root() |
|
+ self._ask_user(""" |
|
+You are about to enable a Copr repository. Please note that this |
|
+repository is not part of the main Fedora distribution, and quality |
|
+may vary. |
|
+ |
|
+The Fedora Project does not exercise any power over the contents of |
|
+this repository beyond the rules outlined in the Copr FAQ at |
|
+<https://fedorahosted.org/copr/wiki/UserDocs#WhatIcanbuildinCopr>, and |
|
+packages are not held to any quality or securty level. |
|
+ |
|
+Please do not file bug reports about these packages in Fedora |
|
+Bugzilla. In case of problems, contact the owner of this repository. |
|
+ |
|
+Do you want to continue? [y/N]: """) |
|
+ self._download_repo(project_name, repo_filename, chroot) |
|
+ base.logger.info(_("Repository successfully enabled.")) |
|
+ elif subcommand == "disable": |
|
+ self._need_root() |
|
+ self._remove_repo(repo_filename) |
|
+ base.logger.info(_("Repository successfully disabled.")) |
|
+ elif subcommand == "list": |
|
+ #http://copr.fedoraproject.org/api/coprs/ignatenkobrain/ |
|
+ api_path = "/api/coprs/{}/".format(project_name) |
|
+ |
|
+ opener = urllib.FancyURLopener({}) |
|
+ res = opener.open(self.copr_url + api_path) |
|
+ try: |
|
+ json_parse = json.loads(res.read()) |
|
+ except ValueError: |
|
+ raise YError( |
|
+ _("Can't parse repositories for username '{}'.") |
|
+ .format(project_name)) |
|
+ section_text = _("List of {} coprs").format(project_name) |
|
+ self._print_match_section(section_text) |
|
+ i = 0 |
|
+ while i < len(json_parse["repos"]): |
|
+ msg = "{0}/{1} : ".format(project_name, |
|
+ json_parse["repos"][i]["name"]) |
|
+ desc = json_parse["repos"][i]["description"] |
|
+ if not desc: |
|
+ desc = _("No description given") |
|
+ msg = self.base.fmtKeyValFill(unicode(msg), desc) |
|
+ print(msg) |
|
+ i += 1 |
|
+ elif subcommand == "search": |
|
+ #http://copr.fedoraproject.org/api/coprs/search/tests/ |
|
+ api_path = "/api/coprs/search/{}/".format(project_name) |
|
+ |
|
+ opener = urllib.FancyURLopener({}) |
|
+ res = opener.open(self.copr_url + api_path) |
|
+ try: |
|
+ json_parse = json.loads(res.read()) |
|
+ except ValueError: |
|
+ raise YError(_("Can't parse search for '{}'.").format(project_name)) |
|
+ section_text = _("Matched: {}").format(project_name) |
|
+ self._print_match_section(section_text) |
|
+ i = 0 |
|
+ while i < len(json_parse["repos"]): |
|
+ msg = "{0}/{1} : ".format(json_parse["repos"][i]["username"], json_parse["repos"][i]["coprname"]) |
|
+ desc = json_parse["repos"][i]["description"] |
|
+ if not desc: |
|
+ desc = _("No description given.") |
|
+ msg = self.base.fmtKeyValFill(unicode(msg), desc) |
|
+ print(msg) |
|
+ i += 1 |
|
+ else: |
|
+ raise YError( |
|
+ _('Unknown subcommand {}.').format(subcommand)) |
|
+ |
|
+ return 0, [basecmd + ' done'] |
|
+ |
|
+ def _print_match_section(self, text): |
|
+ formatted = self.base.fmtSection(text) |
|
+ print(formatted) |
|
+ |
|
+ def _ask_user(self, question): |
|
+ if self.base.conf.assumeyes and not self.base.conf.assumeno: |
|
+ return |
|
+ elif self.base.conf.assumeno and not self.base.conf.assumeyes: |
|
+ raise YError(_('Safe and good answer. Exiting.')) |
|
+ |
|
+ answer = raw_input(question).lower() |
|
+ answer = _(answer) |
|
+ while not ((answer in yes) or (answer in no)): |
|
+ answer = raw_input(question).lower() |
|
+ answer = _(answer) |
|
+ if answer in yes: |
|
+ return |
|
+ else: |
|
+ raise YError(_('Safe and good answer. Exiting.')) |
|
+ |
|
+ @classmethod |
|
+ def _need_root(cls): |
|
+ # FIXME this should do dnf itself (BZ#1062889) |
|
+ if os.geteuid() != 0: |
|
+ raise YError( |
|
+ _('This command has to be run under the root user.')) |
|
+ |
|
+ @classmethod |
|
+ def _guess_chroot(cls): |
|
+ """ Guess which choot is equivalent to this machine """ |
|
+ # FIXME Copr should generate non-specific arch repo |
|
+ dist = platform.linux_distribution() |
|
+ if "Fedora" in dist: |
|
+ # x86_64 because repo-file is same for all arch |
|
+ # ($basearch is used) |
|
+ if "Rawhide" in dist: |
|
+ chroot = ("fedora-rawhide-x86_64") |
|
+ else: |
|
+ chroot = ("fedora-{}-x86_64".format(dist[1])) |
|
+ else: |
|
+ chroot = ("epel-%s-x86_64" % dist[1].split(".", 1)[0]) |
|
+ return chroot |
|
+ |
|
+ @classmethod |
|
+ def _download_repo(cls, project_name, repo_filename, chroot=None): |
|
+ if chroot is None: |
|
+ chroot = cls._guess_chroot() |
|
+ #http://copr.fedoraproject.org/coprs/larsks/rcm/repo/epel-7-x86_64/ |
|
+ api_path = "/coprs/{0}/repo/{1}/".format(project_name, chroot) |
|
+ ug = grabber.URLGrabber() |
|
+ # FIXME when we are full on python2 urllib.parse |
|
+ try: |
|
+ ug.urlgrab(cls.copr_url + api_path, filename=repo_filename) |
|
+ except grabber.URLGrabError as e: |
|
+ cls._remove_repo(repo_filename) |
|
+ raise YError(str(e)) |
|
+ |
|
+ @classmethod |
|
+ def _remove_repo(cls, repo_filename): |
|
+ # FIXME is it Copr repo ? |
|
+ try: |
|
+ os.remove(repo_filename) |
|
+ except OSError as e: |
|
+ raise YError(str(e)) |
|
+ |
|
+ @classmethod |
|
+ def _get_data(cls, req): |
|
+ """ Wrapper around response from server |
|
+ |
|
+ check data and print nice error in case of some error (and return None) |
|
+ otherwise return json object. |
|
+ """ |
|
+ try: |
|
+ output = json.loads(req.text) |
|
+ except ValueError: |
|
+ YCliError(_("Unknown response from server.")) |
|
+ return |
|
+ if req.status_code != 200: |
|
+ YCliError(_( |
|
+ "Something went wrong:\n {0}\n".format(output["error"]))) |
|
+ return |
|
+ return output |
|
+ |
|
+ |
|
+class PlaygroundCommand(CoprCommand): |
|
+ """ Playground plugin for DNF """ |
|
+ |
|
+ aliases = ("playground",) |
|
+ summary = _("Interact with Playground repository.") |
|
+ usage = " [enable|disable|upgrade]" |
|
+ |
|
+ def _cmd_enable(self, chroot): |
|
+ self._need_root() |
|
+ self._ask_user(""" |
|
+You are about to enable a Playground repository. |
|
+ |
|
+Do you want to continue? [y/N]: """) |
|
+ api_url = "{0}/api/playground/list/".format( |
|
+ self.copr_url) |
|
+ req = requests.get(api_url) |
|
+ output = self._get_data(req) |
|
+ if output["output"] != "ok": |
|
+ raise YCliError(_("Unknown response from server.")) |
|
+ for repo in output["repos"]: |
|
+ project_name = "{0}/{1}".format(repo["username"], |
|
+ repo["coprname"]) |
|
+ repo_filename = "/etc/yum.repos.d/_playground_{}.repo" \ |
|
+ .format(project_name.replace("/", "-")) |
|
+ try: |
|
+ # check if that repo exist? but that will result in twice |
|
+ # up calls |
|
+ api_url = "{0}/api/coprs/{1}/detail/{2}/".format( |
|
+ self.copr_url, project_name, chroot) |
|
+ req = requests.get(api_url) |
|
+ output2 = self._get_data(req) |
|
+ if output2 and ("output" in output2) and (output2["output"] == "ok"): |
|
+ self._download_repo(project_name, repo_filename, chroot) |
|
+ except YError: |
|
+ # likely 404 and that repo does not exist |
|
+ pass |
|
+ |
|
+ def _cmd_disable(self): |
|
+ self._need_root() |
|
+ for repo_filename in glob.glob('/etc/yum.repos.d/_playground_*.repo'): |
|
+ self._remove_repo(repo_filename) |
|
+ |
|
+ def doCommand(self, base, basecmd, extcmds): |
|
+ try: |
|
+ subcommand = extcmds[0] |
|
+ except (ValueError, IndexError): |
|
+ base.logger.critical( |
|
+ _('Error: ') + |
|
+ _('exactly one parameter to ' |
|
+ 'playground command are required')) |
|
+ # FIXME: |
|
+ # dnf.cli.commands.err_mini_usage(self.cli, self.cli.base.basecmd) |
|
+ raise YCliError( |
|
+ _('exactly one parameter to ' |
|
+ 'playground command are required')) |
|
+ chroot = self._guess_chroot() |
|
+ if subcommand == "enable": |
|
+ self._cmd_enable(chroot) |
|
+ base.logger.info(_("Playground repositories successfully enabled.")) |
|
+ elif subcommand == "disable": |
|
+ self._cmd_disable() |
|
+ base.logger.info(_("Playground repositories successfully disabled.")) |
|
+ elif subcommand == "upgrade": |
|
+ self._cmd_disable() |
|
+ self._cmd_enable(chroot) |
|
+ base.logger.info(_("Playground repositories successfully updated.")) |
|
+ else: |
|
+ raise YError( |
|
+ _('Unknown subcommand {}.').format(subcommand)) |
|
+ |
|
+ return 0, [basecmd + ' done']
|
|
|