Repository :
http://git.fedorahosted.org/cgit/copr.git
On branch : master
---------------------------------------------------------------
commit f44be6b8e9f532187e02c363bfd797f4068870d5
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Thu Sep 25 17:19:40 2014 +0200
[python-copr, cli] updating copr-cli to use python-copr
---------------------------------------------------------------
cli/copr_cli/README.rst | 12 +-
cli/copr_cli/main.py | 123 ++++++++++++++-----
cli/documentation/Makefile | 34 ------
.../how-to-generate-documentation.txt | 10 --
frontend/coprs_frontend/tests/test_helpers.py | 1 -
python/copr/client/client.py | 42 +++++--
python/copr/test/test_client.py | 6 +
7 files changed, 134 insertions(+), 94 deletions(-)
diff --git a/cli/copr_cli/README.rst b/cli/copr_cli/README.rst
index e46f5ff..b1488f8 100644
--- a/cli/copr_cli/README.rst
+++ b/cli/copr_cli/README.rst
@@ -29,22 +29,24 @@ The CLI depends on:
Usage:
------
-.. _test instance:
http://copr-fe.cloud.fedoraproject.org/
+.. _test instance:
http://copr-fe-dev.cloud.fedoraproject.org/
- Create an account on copr `test instance`_
-- Go to the API page:
http://copr-fe.cloud.fedoraproject.org/api
+- Go to the API page:
http://copr.fedoraproject.org/api
- Retrieve your API token
- Create the file ``~/.config/copr``
- In this file add the following content
+(simpler way is just to copy it from /api)
::
[copr-cli]
- username = <insert here your API login>
+ username = <insert here your login>
+ login = <insert here your API login>
token = <insert here your API token>
+ copr_url =
https://copr.fedoraproject.org
-
-You should then be able to use copr-cli to list, create and build on copr.
+You should be able to use copr-cli to list, create and build on copr.
.. note:: You can use directly copr-cli to list someone's copr repo but to create
a copr or build packages into an existing repo you need to authenticate
diff --git a/cli/copr_cli/main.py b/cli/copr_cli/main.py
index e4c617e..3f9c97d 100644
--- a/cli/copr_cli/main.py
+++ b/cli/copr_cli/main.py
@@ -1,4 +1,5 @@
-#-*- coding: UTF-8 -*-
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
__version__ = "0.3.0"
__description__ = "CLI tool to run copr"
@@ -14,47 +15,97 @@ from copr import CoprClient
import copr.client.exceptions as copr_exceptions
+no_config_warning = """
+|================ WARNING: =======================|
+|File '~/.config/copr' is missing or incorrect. |
+| See documentation: man copr-cli. |
+| Any operation requiring credentionals will fail!|
+|=================================================|
+
+"""
+
+no_config = True
try:
client = CoprClient.create_from_file_config()
-except copr_exceptions.CoprNoConfException as e:
- print(e)
- sys.exit(1)
+ no_config = False
+except (copr_exceptions.CoprNoConfException,
+ copr_exceptions.CoprConfigException) as e:
+ print(no_config_warning)
+ client = CoprClient({
+ "copr_url": "http://copr.fedoraproject.org"
+ })
+
+
+def check_username_presence(func):
+ def wrapper(args):
+ if 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 client.username is None:
+ print("Error: Operation requires username\n"
+ "Pass username to command or add it to `~/.config/copr`")
+
+ return func(args)
+
+ wrapper.__doc__ = func.__doc__
+ wrapper.__name__ = func.__name__
+ return wrapper
+
+
+def requires_api_auth(func):
+ def wrapper(args):
+ if no_config:
+ print("Error: Operation requires api authentication\n"
+ "File `~/.config/copr` is missing or incorrect")
+ sys.exit(6)
+ return func(args)
-def _watch_builds(build_ids):
+ wrapper.__doc__ = func.__doc__
+ wrapper.__name__ = func.__name__
+ return wrapper
+
+
+def _watch_builds(builds_list):
+ """
+ :param builds_list: list of BuildWrapper
+
+ """
print("Watching build(s): (this may be safely interrupted)")
prevstatus = defaultdict(lambda: None)
failed_ids = []
- watched_ids = build_ids[:]
+ watched_ids = [bw.build_id for bw in builds_list]
try:
while True:
for build_id in watched_ids:
- build_status = client.get_build_status(build_id)
- if build_status.output != "ok":
- errmsg = " Build {1}: Unable to get build status:
{0}".format(
- build_status.error, build_id)
+ build_details = 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_status.status:
- prevstatus[build_id] = build_status.status
+ 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_status.status, build_id))
+ build_details.status, build_id))
- if build_status.status in ["failed"]:
+ if build_details.status in ["failed"]:
failed_ids.append(build_id)
- if build_status.status in ["succeeded", "skipped",
+ if build_details.status in ["succeeded", "skipped",
"failed", "canceled"]:
watched_ids.remove(build_id)
- if build_status.status == "unknown":
+ if build_details.status == "unknown":
raise copr_exceptions.CoprBuildException(
"Unknown status.")
if not watched_ids:
break
- time.sleep(60)
+ time.sleep(3)
if failed_ids:
raise copr_exceptions.CoprBuildException(
@@ -65,6 +116,7 @@ def _watch_builds(build_ids):
pass
+@requires_api_auth
def action_build(args):
""" Method called when the 'build' action has been selected by
the
user.
@@ -79,12 +131,13 @@ def action_build(args):
print(result.error)
return
print(result.message)
- print("Created builds: {0}".format(" ".join(map(str,
result.ids))))
+ print("Created builds: {0}".format(" ".join(map(str,
result.builds_list))))
if not args.nowait:
- _watch_builds(result.ids)
+ _watch_builds(result.builds_list)
+@requires_api_auth
def action_create(args):
""" Method called when the 'create' action has been selected
by the
user.
@@ -96,9 +149,10 @@ def action_create(args):
projectname=args.name, description=args.description,
instructions=args.instructions, chroots=args.chroots,
repos=args.repos, initial_pkgs=args.initial_pkgs)
- print(result)
+ print(result.message)
+@requires_api_auth
def action_delete(args):
""" Method called when the 'delete' action has been selected
by the
user.
@@ -106,9 +160,10 @@ def action_delete(args):
:param args: argparse arguments provided by the user
"""
result = client.delete_project(projectname=args.copr)
- print(result)
+ print(result.message)
+@check_username_presence
def action_list(args):
""" Method called when the 'list' action has been selected by
the
user.
@@ -118,29 +173,31 @@ def action_list(args):
"""
username = args.username or client.username
result = client.get_projects_list(username)
- if result.response["output"] != "ok":
+ if result.output != "ok":
print(result.error)
print("Un-expected data returned, please report this issue")
- elif not result.projects:
+ elif not result.projects_list:
print("No copr retrieved for user: '{0}'".format(username))
return
- for prj in result.projects:
+ for prj in result.projects_list:
print(prj)
def action_status(args):
- result = client.get_build_status(args.build_id)
- print(result)
+ result = client.get_build_details(args.build_id)
+ print(result.status)
+@requires_api_auth
def action_cancel(args):
""" Method called when the 'cancel' action has been selected
by the
user.
:param args: argparse arguments provided by the user
"""
result = client.cancel_build(args.build_id)
- print(result)
+ print(result.status)
+
def setup_parser():
"""
@@ -237,24 +294,24 @@ def main(argv=sys.argv[1:]):
except KeyboardInterrupt:
sys.stderr.write("\nInterrupted by user.")
sys.exit(1)
- except copr_exceptions.CoprRequestException, e:
+ except copr_exceptions.CoprRequestException as e:
sys.stderr.write("\nSomething went wrong:")
sys.stderr.write("\nError: {0}\n".format(e))
sys.exit(1)
- except argparse.ArgumentTypeError, e:
+ except argparse.ArgumentTypeError as e:
sys.stderr.write("\nError: {0}".format(e))
sys.exit(2)
- except copr_exceptions.CoprException, e:
+ except copr_exceptions.CoprException as e:
sys.stderr.write("\nError: {0}\n".format(e))
sys.exit(3)
- except ConfigParser.ParsingError, e:
+ except ConfigParser.ParsingError as e:
sys.stderr.write("\nError: {0}\n".format(e))
sys.stderr.write("Lines in INI file should not be indented.\n")
sys.exit(3)
- except copr_exceptions.CoprBuildException, e:
+ except copr_exceptions.CoprBuildException as e:
sys.stderr.write("\nBuild error: {0}\n".format(e))
sys.exit(4)
- except copr_exceptions.CoprUnknownResponseException, e:
+ except copr_exceptions.CoprUnknownResponseException as e:
sys.stderr.write("\nError: {0}\n".format(e))
sys.exit(5)
# except Exception as e:
diff --git a/cli/documentation/Makefile b/cli/documentation/Makefile
deleted file mode 100644
index d866287..0000000
--- a/cli/documentation/Makefile
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2008, Steve 'Ashcrow' Milner <smilner(a)redhat.com>
-#
-# This software may be freely redistributed under the terms of the GNU
-# general public license.
-#
-# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-BUILD_DIR := build
-
-# Python doc related
-EPYDOC_BIN := `which epydoc`
-EPYDOC_SWITCHES := --inheritance listed --graph all --html
-PYTHON_DOC_DIR := python-doc
-PYTHON_DIRS := ../copr_cli
-
-# TARGETS
-# schema is not incldued here, since you need a live db
-all: python
-
-python:
- mkdir -p $(BUILD_DIR)/py-doc-build
- $(SHELL ulimit -n 4096)
- echo "[epydoc]" > $(BUILD_DIR)/epydoc.lst
- echo -n "modules: " >> $(BUILD_DIR)/epydoc.lst
- find $(PYTHON_DIRS) -type f -name '*.py' | grep -v test | xargs echo -n >>
$(BUILD_DIR)/epydoc.lst
- $(EPYDOC_BIN) $(EPYDOC_SWITCHES) -o $(PYTHON_DOC_DIR) --config $(BUILD_DIR)/epydoc.lst
-
-clean:
- rm -rf build/
-
-distclean:
- rm -rf build/ $(PYTHON_DOC_DIR) $(JAVA_DOC_DIR) $(SCHEMA_DOC_DIR)
diff --git a/cli/documentation/how-to-generate-documentation.txt
b/cli/documentation/how-to-generate-documentation.txt
deleted file mode 100644
index 3af5b30..0000000
--- a/cli/documentation/how-to-generate-documentation.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Documentation of python:
- # epydoc needs to be installed, it needs texlive, ouch :-(
- # graphviz needs to be installed.
- # In the Copr git repo checkout, in documentation/ directory:
- git rm -rf python-doc
- make python
- git add python-doc
- rm -rf build
- # update python-doc.readme
- git commit -m 'Updating python documentation.' .
diff --git a/frontend/coprs_frontend/tests/test_helpers.py
b/frontend/coprs_frontend/tests/test_helpers.py
index df019da..4e41748 100644
--- a/frontend/coprs_frontend/tests/test_helpers.py
+++ b/frontend/coprs_frontend/tests/test_helpers.py
@@ -99,7 +99,6 @@ class TestHelpers(CoprsTestCase):
raise e
app.config["ENFORCE_PROTOCOL_FOR_BACKEND_URL"] = orig
-
def test_fix_protocol_for_frontend(self):
http_url = "http://example.com/repo"
https_url = "https://example.com/repo"
diff --git a/python/copr/client/client.py b/python/copr/client/client.py
index 170bdd1..9d8c385 100644
--- a/python/copr/client/client.py
+++ b/python/copr/client/client.py
@@ -31,6 +31,22 @@ from .parsers import fabric_simple_fields_parser, ProjectListParser, \
ProjectDetailsFieldsParser
+## add deco to check that login/token are provided
+## and raise correct error
+##
+##
+""" "No configuration file '~/.config/copr' found. "
+ "see documentation at /usr/share/doc/python-copr/ "
+"""
+# or
+"""
+ "No api login and\or api token are provided"
+ "See man copr-cli for more information")
+"""
+
+##
+
+
class CoprClient(object):
""" Main interface to the copr service
@@ -55,6 +71,8 @@ class CoprClient(object):
self.username = config.get("username")
self.copr_url = config.get("copr_url",
"http://copr.fedoraproject.org/")
+ self.no_config = config.get("no_config", False)
+
def __str__(self):
return "<Copr client. username: {0}, api url: {1}>".format(
self.username, self.api_url
@@ -84,19 +102,21 @@ class CoprClient(object):
filepath = os.path.join(os.path.expanduser("~"),
".config", "copr")
config = {}
if not raw_config.read(filepath):
- raise CoprNoConfException(
+ log.warning(
"No configuration file '~/.config/copr' found. "
"See man copr-cli for more information")
- try:
- for field in ["username", "login", "token",
"copr_url"]:
- if six.PY3:
- config[field] = raw_config["copr-cli"].get(field, None)
- else:
- config[field] = raw_config.get("copr-cli", field, None)
-
- except configparser.Error as err:
- raise CoprConfigException(
- "Bad configuration file: {0}".format(err))
+ config["no_config"] = True
+ else:
+ try:
+ for field in ["username", "login", "token",
"copr_url"]:
+ if six.PY3:
+ config[field] = raw_config["copr-cli"].get(field,
None)
+ else:
+ config[field] = raw_config.get("copr-cli", field,
None)
+
+ except configparser.Error as err:
+ raise CoprConfigException(
+ "Bad configuration file: {0}".format(err))
return CoprClient(config=config)
def _fetch(self, url, data=None, projectname=None, username=None,
diff --git a/python/copr/test/test_client.py b/python/copr/test/test_client.py
index bea3179..6d73f97 100644
--- a/python/copr/test/test_client.py
+++ b/python/copr/test/test_client.py
@@ -33,6 +33,12 @@ def test_client_from_config():
assert cl.token == "api-token"
assert cl.username == "user_name"
+(a)mock.patch('requests.request')
+def test_list_projects(mock_request):
+ mock_client = CoprClient.create_from_file_config(config_location)
+
+
+
# TODO: package
https://github.com/dropbox/responses and use it
def make_mock_response(filename, status_code=None):