Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-osc-tiny for openSUSE:Factory checked in at 2022-07-21 11:33:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-osc-tiny (Old) and /work/SRC/openSUSE:Factory/.python-osc-tiny.new.1523 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-osc-tiny" Thu Jul 21 11:33:08 2022 rev:16 rq:990423 version:0.6.5 Changes: -------- --- /work/SRC/openSUSE:Factory/python-osc-tiny/python-osc-tiny.changes 2022-07-03 18:26:53.320735019 +0200 +++ /work/SRC/openSUSE:Factory/.python-osc-tiny.new.1523/python-osc-tiny.changes 2022-07-21 11:33:37.402961354 +0200 @@ -1,0 +2,22 @@ +Wed Jul 20 08:49:57 UTC 2022 - Andreas Hasenkopf <ahasenk...@suse.com> + +- Release 0.6.5: Enhanced dependency management + * Removed `responses` as requirement + * Use `cached_property` instead of `backports.cached_property` + (which is not in the openSUSE repos) + * Only install `cached_property` as requirement for Py3.7 and earlier + +------------------------------------------------------------------- +Tue Jul 19 07:01:51 UTC 2022 - Andreas Hasenkopf <ahasenk...@suse.com> + +- Release 0.6.4 + * Handle 40x (x!=1) responses properly in `HttpSignatureAuth` + +------------------------------------------------------------------- +Mon Jul 18 07:55:16 UTC 2022 - Andreas Hasenkopf <ahasenk...@suse.com> + +- Release 0.6.3 + * Do not assume that `oscrc` contains the SSH passphrase + * Handle absence of `sshkey` in `.oscrc` gracefully + +------------------------------------------------------------------- Old: ---- osc-tiny-0.6.2.tar.gz New: ---- osc-tiny-0.6.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-osc-tiny.spec ++++++ --- /var/tmp/diff_new_pack.ZFXFJ6/_old 2022-07-21 11:33:38.058962001 +0200 +++ /var/tmp/diff_new_pack.ZFXFJ6/_new 2022-07-21 11:33:38.062962005 +0200 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-osc-tiny -Version: 0.6.2 +Version: 0.6.5 Release: 0 Summary: Client API for openSUSE BuildService License: MIT @@ -27,7 +27,6 @@ URL: https://github.com/crazyscientist/osc-tiny Source: https://files.pythonhosted.org/packages/source/o/osc-tiny/osc-tiny-%{version}.tar.gz BuildRequires: %{python_module PyYAML} -BuildRequires: %{python_module cached-property} BuildRequires: %{python_module devel} BuildRequires: %{python_module lxml} BuildRequires: %{python_module pytest} @@ -39,11 +38,14 @@ BuildRequires: fdupes BuildRequires: python-rpm-macros Requires: python-PyYAML -Requires: python-cached-property Requires: python-lxml Requires: python-python-dateutil Requires: python-pytz Requires: python-requests +%if %python_version_nodots < 38 +BuildRequires: python3-cached-property +Requires: python-cached-property +%endif Suggests: openssh BuildArch: noarch %python_subpackages ++++++ osc-tiny-0.6.2.tar.gz -> osc-tiny-0.6.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/PKG-INFO new/osc-tiny-0.6.5/PKG-INFO --- old/osc-tiny-0.6.2/PKG-INFO 2022-06-30 10:46:34.485521000 +0200 +++ new/osc-tiny-0.6.5/PKG-INFO 2022-07-20 10:38:48.407163100 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: osc-tiny -Version: 0.6.2 +Version: 0.6.5 Summary: Client API for openSUSE BuildService Home-page: http://github.com/crazyscientist/osc-tiny Download-URL: http://github.com/crazyscientist/osc-tiny/tarball/master diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/osc_tiny.egg-info/PKG-INFO new/osc-tiny-0.6.5/osc_tiny.egg-info/PKG-INFO --- old/osc-tiny-0.6.2/osc_tiny.egg-info/PKG-INFO 2022-06-30 10:46:33.000000000 +0200 +++ new/osc-tiny-0.6.5/osc_tiny.egg-info/PKG-INFO 2022-07-20 10:38:48.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: osc-tiny -Version: 0.6.2 +Version: 0.6.5 Summary: Client API for openSUSE BuildService Home-page: http://github.com/crazyscientist/osc-tiny Download-URL: http://github.com/crazyscientist/osc-tiny/tarball/master diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/osc_tiny.egg-info/SOURCES.txt new/osc-tiny-0.6.5/osc_tiny.egg-info/SOURCES.txt --- old/osc-tiny-0.6.2/osc_tiny.egg-info/SOURCES.txt 2022-06-30 10:46:34.000000000 +0200 +++ new/osc-tiny-0.6.5/osc_tiny.egg-info/SOURCES.txt 2022-07-20 10:38:48.000000000 +0200 @@ -3,6 +3,7 @@ README.md requirements.txt requirements_devel.txt +requirements_pre38.txt setup.py osc_tiny.egg-info/PKG-INFO osc_tiny.egg-info/SOURCES.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/osc_tiny.egg-info/requires.txt new/osc-tiny-0.6.5/osc_tiny.egg-info/requires.txt --- old/osc-tiny-0.6.2/osc_tiny.egg-info/requires.txt 2022-06-30 10:46:34.000000000 +0200 +++ new/osc-tiny-0.6.5/osc_tiny.egg-info/requires.txt 2022-07-20 10:38:48.000000000 +0200 @@ -1,7 +1,5 @@ -backports.cached-property lxml requests -responses python-dateutil pytz pyyaml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/osctiny/__init__.py new/osc-tiny-0.6.5/osctiny/__init__.py --- old/osc-tiny-0.6.2/osctiny/__init__.py 2022-06-30 10:46:21.000000000 +0200 +++ new/osc-tiny-0.6.5/osctiny/__init__.py 2022-07-20 10:38:38.000000000 +0200 @@ -6,4 +6,4 @@ __all__ = ['Osc', 'bs_requests', 'buildresults', 'comments', 'packages', 'projects', 'search', 'users'] -__version__ = "0.6.2" +__version__ = "0.6.5" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/osctiny/extensions/origin.py new/osc-tiny-0.6.5/osctiny/extensions/origin.py --- old/osc-tiny-0.6.2/osctiny/extensions/origin.py 2022-06-30 10:46:21.000000000 +0200 +++ new/osc-tiny-0.6.5/osctiny/extensions/origin.py 2022-07-20 10:38:38.000000000 +0200 @@ -35,10 +35,7 @@ from functools import cached_property except ImportError: # Support for Python3 prior 3.8 - try: - from backports.cached_property import cached_property - except ImportError: - from cached_property import cached_property + from cached_property import cached_property try: from yaml import CSafeLoader as SafeLoader diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/osctiny/osc.py new/osc-tiny-0.6.5/osctiny/osc.py --- old/osc-tiny-0.6.2/osctiny/osc.py 2022-06-30 10:46:21.000000000 +0200 +++ new/osc-tiny-0.6.5/osctiny/osc.py 2022-07-20 10:38:38.000000000 +0200 @@ -88,8 +88,8 @@ :param url: API URL of a BuildService instance :param username: Username - :param password: Password; this is either the user password or the SSH passphrase, if - ``ssh_key_file`` is defined + :param password: Password; this is either the user password (``ssh_key_file`` is ``None``) or + the SSH passphrase, if ``ssh_key_file`` is defined :param verify: See `SSL Cert Verification`_ for more details :param cache: Store API responses in a cache :param ssh_key_file: Path to SSH private key file @@ -145,7 +145,7 @@ if not self.username and not self.password and not self.ssh_key: try: self.username, self.password, self.ssh_key = get_credentials(self.url) - except (ValueError, NotImplementedError, FileNotFoundError) as error: + except (ValueError, RuntimeError, FileNotFoundError) as error: raise OscError from error # API endpoints diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/osctiny/utils/auth.py new/osc-tiny-0.6.5/osctiny/utils/auth.py --- old/osc-tiny-0.6.2/osctiny/utils/auth.py 2022-06-30 10:46:21.000000000 +0200 +++ new/osc-tiny-0.6.5/osctiny/utils/auth.py 2022-07-20 10:38:38.000000000 +0200 @@ -6,6 +6,8 @@ """ import typing from base64 import b64decode, b64encode +import logging +import os from pathlib import Path from subprocess import Popen, PIPE import re @@ -43,6 +45,9 @@ super().__init__(username=username, password=password) if not ssh_key_file.is_file(): raise FileNotFoundError(f"SSH key at location does not exist: {ssh_key_file}") + if not password and not self.is_ssh_agent_available(): + raise RuntimeError("SSH signing impossible: No password/passphrase provided and no SSH " + "agent running! ") self.ssh_key_file = ssh_key_file self.pattern = re.compile(r"(?<=\)) (?=\()") @@ -58,6 +63,19 @@ parts = self.pattern.split(headers) return [part.strip("()") for part in parts] + @staticmethod + def is_ssh_agent_available() -> bool: + """ + Check whether SSH agent is running/available + + :return: ``True``, if agent is running + + .. versionadded:: 0.6.3 + """ + relevant_keys = {'SSH_AUTH_SOCK', 'SSH_AGENT_PID'} + overlap = os.environ.keys() & relevant_keys + return len(overlap) > 0 + def ssh_sign(self) -> str: """ Solve the challenge via SSH signing @@ -114,6 +132,16 @@ return "" + def _log(self, r: Response) -> None: + logger = logging.getLogger("osctiny.request") + if logger.level >= logging.CRITICAL: + return + + logger.info("Server replied with status %d", r.status_code) + logger.debug("Response headers:\n%s\n---", "\n".join(f"{k}: {v}" + for k, v in r.headers.items())) + logger.debug("Response content:\n%s\n---", r.text) + def handle_401(self, r: Response, **kwargs) -> Response: """ Handle authentication in case of 401 @@ -124,6 +152,13 @@ self._thread_local.num_401_calls = 1 return r + if r.status_code != 401: + # If this is not a 401 response, the server does not send the authentication headers. + # So there is no point in pretending otherwise. + return r + + self._log(r) + if self._thread_local.pos is not None: # Rewind the file position indicator of the body to where # it was to resend the request. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/osctiny/utils/conf.py new/osc-tiny-0.6.5/osctiny/utils/conf.py --- old/osc-tiny-0.6.2/osctiny/utils/conf.py 2022-06-30 10:46:21.000000000 +0200 +++ new/osc-tiny-0.6.5/osctiny/utils/conf.py 2022-07-20 10:38:38.000000000 +0200 @@ -14,7 +14,6 @@ from configparser import ConfigParser, NoSectionError import os from pathlib import Path -import warnings try: from osc import conf as _conf @@ -45,52 +44,25 @@ raise FileNotFoundError("No `osc` configuration file found") -# pylint: disable=too-many-branches -def get_credentials(url: typing.Optional[str] = None) \ - -> typing.Tuple[str, str, typing.Optional[Path]]: +def _get_credentials_from_oscrc(url: typing.Optional[str] = None) -> typing.Tuple[str, str, Path]: """ - Get credentials for Build Service instance identified by ``url`` + Get credentials for Build Service instance identified by ``url`` from ``osc`` config file - .. important:: + .. note:: - If the ``osc`` package is not installed, this function will only try to extract the username - and password from the configuration file. + This function does not perform data validation or sanitation. It is not recommended to call + this function directly; use :py:fun:`get_credentials` instead. - Any credentials stored on a keyring will not be accessible! - - :param str url: URL of Build Service instance (including schema). If not specified, the value - from the ``apiurl`` parameter in the config file will be used. + :param url: URL of Build Service instance (including schema). If not specified, the value + from the ``apiurl`` parameter in the config file will be used. :return: (username, password, SSH private key path) :raises ValueError: if config provides no credentials - """ - if _conf is not None: - try: - _conf.get_config() - if url is None: - # get the default api url from osc's config - url = _conf.config["apiurl"] - # and now fetch the options for that particular url - api_config = _conf.get_apiurl_api_host_options(url) - username = api_config["user"] - password = api_config["pass"] - sshkey = Path(api_config["sshkey"]) if api_config["sshkey"] else None - except (ConfigError, ConfigMissingApiurl) as error: - if isinstance(error, ConfigError): - raise ValueError("`osc` config was not found.") from error - # this is the case of ConfigMissingApiurl - raise ValueError("`osc` config has no options for URL {}".format(url)) from error - - if not username: - raise ValueError("`osc` config provides no username for URL {}".format(url)) - if not password: - raise ValueError("`osc` config provides no password for URL {}".format(url)) - return username, password, sshkey - warnings.warn("`osc` is not installed. Not all configuration backends of `osc` will be " - "available.") + .. versionadded:: 0.6.3 + """ parser = ConfigParser() path = get_config_path() - parser.read((path)) + parser.read(path) try: if url is None: url = parser["general"].get("apiurl", url) @@ -101,8 +73,6 @@ raise ValueError("`osc` config has no section for URL {}".format(url)) username = parser[url].get("user", None) - if not username: - raise ValueError("`osc` config provides no username for URL {}".format(url)) password = parser[url].get("pass", None) if not password: @@ -110,11 +80,84 @@ if password: password = decompress(b64decode(password.encode("ascii"))).decode("ascii") - if not password: - raise ValueError("`osc` config provides no password for URL {}".format(url)) - sshkey = parser[url].get("sshkey", None) if sshkey: - sshkey = Path(sshkey) + sshkey = Path(sshkey).expanduser() return username, password, sshkey + + +def _get_credentials_from_oscconf(url: typing.Optional[str] = None) -> typing.Tuple[str, str, Path]: + """ + Get credentials for Build Service instance identified by ``url`` from ``osc`` + + .. note:: + + This function does not perform data validation or sanitation. It is not recommended to call + this function directly; use :py:fun:`get_credentials` instead. + + :param url: URL of Build Service instance (including schema). If not specified, the value + from the ``apiurl`` parameter in the config file will be used. + :return: (username, password, SSH private key path) + :raises ValueError: if config provides no credentials + :raises RuntimeError: if ``osc`` is not installed + + .. versionadded:: 0.6.3 + """ + if _conf is None: + raise RuntimeError("`osc` is not installed. Use _get_credentials_from_oscrc instead!") + try: + _conf.get_config() + if url is None: + # get the default api url from osc's config + url = _conf.config["apiurl"] + # and now fetch the options for that particular url + api_config = _conf.get_apiurl_api_host_options(url) + username = api_config["user"] + password = api_config["pass"] + sshkey = Path(api_config["sshkey"]) if api_config.get("sshkey", None) else None + except (KeyError, ConfigError, ConfigMissingApiurl) as error: + if isinstance(error, ConfigError): + raise ValueError("`osc` config was not found.") from error + # this is the case of ConfigMissingApiurl + raise ValueError("`osc` config has no options for URL {}".format(url)) from error + + return username, password, sshkey + + +# pylint: disable=too-many-branches +def get_credentials(url: typing.Optional[str] = None) \ + -> typing.Tuple[str, typing.Optional[str], typing.Optional[Path]]: + """ + Get credentials for Build Service instance identified by ``url`` + + .. important:: + + If the ``osc`` package is not installed, this function will only try to extract the username + and password from the configuration file. + + Any credentials stored on a keyring will not be accessible! + + :param url: URL of Build Service instance (including schema). If not specified, the value + from the ``apiurl`` parameter in the config file will be used. + :return: (username, password, SSH private key path) + :raises ValueError: if config provides no credentials + + .. versionchanged:: 0.6.3 + + If an SSH key is configured, this function will return ``None`` instead of a password. + """ + getter = _get_credentials_from_oscrc if _conf is None else _get_credentials_from_oscconf + username, password, sshkey = getter(url=url) + + if not username: + raise ValueError(f"`osc` config provides no username for URL {url}") + + if sshkey is not None: + if not sshkey.exists(): + raise ValueError(f"SSH key from config does not exist: {sshkey}") + + if not password and not sshkey: + raise ValueError(f"`osc` config provides no password or SSH key for URL {url}") + + return username, password if sshkey is None else None, sshkey diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/requirements.txt new/osc-tiny-0.6.5/requirements.txt --- old/osc-tiny-0.6.2/requirements.txt 2022-06-30 10:46:21.000000000 +0200 +++ new/osc-tiny-0.6.5/requirements.txt 2022-07-20 10:38:38.000000000 +0200 @@ -1,7 +1,5 @@ -backports.cached-property lxml requests -responses python-dateutil pytz -pyyaml \ No newline at end of file +pyyaml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/requirements_devel.txt new/osc-tiny-0.6.5/requirements_devel.txt --- old/osc-tiny-0.6.2/requirements_devel.txt 2022-06-30 10:46:21.000000000 +0200 +++ new/osc-tiny-0.6.5/requirements_devel.txt 2022-07-20 10:38:38.000000000 +0200 @@ -1,3 +1,4 @@ pylint Sphinx sphinx_rtd_theme +responses diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/requirements_pre38.txt new/osc-tiny-0.6.5/requirements_pre38.txt --- old/osc-tiny-0.6.2/requirements_pre38.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/osc-tiny-0.6.5/requirements_pre38.txt 2022-07-20 10:38:38.000000000 +0200 @@ -0,0 +1 @@ +cached-property diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/osc-tiny-0.6.2/setup.py new/osc-tiny-0.6.5/setup.py --- old/osc-tiny-0.6.2/setup.py 2022-06-30 10:46:21.000000000 +0200 +++ new/osc-tiny-0.6.5/setup.py 2022-07-20 10:38:38.000000000 +0200 @@ -1,16 +1,23 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +from sys import version_info from setuptools import setup, find_packages def get_requires(): + requirements = [] + def _filter(requires): return [req.strip() for req in requires if req.strip()] - filename = "requirements.txt" + with open("requirements.txt", "r") as fh: + requirements += _filter(fh.readlines()) + + if version_info.minor < 8: + with open("requirements_pre38.txt", "r") as fh: + requirements += _filter(fh.readlines()) - with open(filename, "r") as fh: - return _filter(fh.readlines()) + return requirements with open("README.md") as fh: @@ -19,7 +26,7 @@ setup( name='osc-tiny', - version='0.6.2', + version='0.6.5', description='Client API for openSUSE BuildService', long_description=long_description, long_description_content_type="text/markdown",