Commit 630eb9e7 authored by Mark Hewitt's avatar Mark Hewitt
Browse files

Merge branch 'feature/update_check' into 'main'

Update check when running 'osdu' or 'osdu version'

Closes #12

See merge request !13
parents c34f5f58 4647b6f1
Pipeline #86410 passed with stages
in 3 minutes and 5 seconds
...@@ -28,7 +28,13 @@ For more information, specify the `-h` flag: ...@@ -28,7 +28,13 @@ For more information, specify the `-h` flag:
Change Log Change Log
========== ==========
0.0.31
------
- Added update check when running 'osdu' or 'osdu version'
0.0.30 0.0.30
------
- API documentation pages are shown in info commands - API documentation pages are shown in info commands
- workflow get, runs and status commands - workflow get, runs and status commands
......
...@@ -12,4 +12,4 @@ ...@@ -12,4 +12,4 @@
""" OSDU command line environment""" """ OSDU command line environment"""
__VERSION__ = "0.0.30" __VERSION__ = "0.0.31"
...@@ -18,7 +18,7 @@ import pkgutil ...@@ -18,7 +18,7 @@ import pkgutil
import click import click
from osducli.click_cli import CustomClickGroup, State from osducli.click_cli import CustomClickGroup, CustomMainClickGroup, State
def get_commands_from_pkg(pkg) -> dict: def get_commands_from_pkg(pkg) -> dict:
...@@ -71,7 +71,7 @@ def get_commands_from_pkg(pkg) -> dict: ...@@ -71,7 +71,7 @@ def get_commands_from_pkg(pkg) -> dict:
# Main entry point for OSDU CLI. # Main entry point for OSDU CLI.
# noqa: W606,W605 pylint: disable=W1401 # noqa: W606,W605 pylint: disable=W1401
@click.group( @click.group(
cls=CustomClickGroup, cls=CustomMainClickGroup,
commands=get_commands_from_pkg("osducli.commands"), commands=get_commands_from_pkg("osducli.commands"),
context_settings={"help_option_names": ["-h", "--help"]}, context_settings={"help_option_names": ["-h", "--help"]},
) )
......
...@@ -24,9 +24,11 @@ import click ...@@ -24,9 +24,11 @@ import click
from click.core import Context from click.core import Context
from click.formatting import HelpFormatter from click.formatting import HelpFormatter
import osducli
from osducli.config import CLI_ENV_VAR_PREFIX, CLIConfig from osducli.config import CLI_ENV_VAR_PREFIX, CLIConfig
from osducli.log import get_logger from osducli.log import get_logger
from osducli.state import get_default_config from osducli.state import get_default_config
from osducli.util.pypi import get_pypi_version
class State: # pylint: disable=too-few-public-methods class State: # pylint: disable=too-few-public-methods
...@@ -80,6 +82,34 @@ class CustomClickGroup(click.Group): ...@@ -80,6 +82,34 @@ class CustomClickGroup(click.Group):
self.format_commands(ctx, formatter) self.format_commands(ctx, formatter)
class CustomMainClickGroup(CustomClickGroup):
"""Custom click.Group class providing customised help text"""
def format_help(self, ctx: Context, formatter: HelpFormatter) -> None:
"""Writes the help into the formatter if it exists.
This is a low-level method called by :meth:`get_help`.
This calls the following methods:
- :meth:`format_usage`
- :meth:`format_help_text`
- :meth:`format_options`
- :meth:`format_epilog`
"""
super().format_help(ctx, formatter)
current_version = osducli.__VERSION__
latest_version = get_pypi_version("osducli")
if latest_version is not None and current_version != latest_version:
formatter.write_paragraph()
formatter.write_text(
f"\b\n\033[33mWARNING: You are using osdu cli version {current_version}; however version {latest_version} is available.\033[39m" # noqa: E501 pylint: disable=line-too-long
)
formatter.write_text(
"\b\n\033[33mYou should consider upgrading via the 'pip install -U osdu-cli' command\033[39m" # noqa: E501 pylint: disable=line-too-long
)
class CustomClickCommand(click.Command): class CustomClickCommand(click.Command):
"""Custom click.GrouCommand class providing customised help text""" """Custom click.GrouCommand class providing customised help text"""
......
...@@ -28,6 +28,7 @@ from osducli.config import ( ...@@ -28,6 +28,7 @@ from osducli.config import (
CONFIG_STORAGE_URL, CONFIG_STORAGE_URL,
) )
from osducli.log import get_logger from osducli.log import get_logger
from osducli.util.pypi import get_pypi_version
logger = get_logger(__name__) logger = get_logger(__name__)
...@@ -49,8 +50,7 @@ def get_runtime_version() -> str: ...@@ -49,8 +50,7 @@ def get_runtime_version() -> str:
""" """
import platform import platform
version_info = "\n\n" version_info = "Python ({}) {}".format(platform.system(), sys.version)
version_info += "Python ({}) {}".format(platform.system(), sys.version)
version_info += "\n\n" version_info += "\n\n"
version_info += "Python location '{}'".format(sys.executable) version_info += "Python location '{}'".format(sys.executable)
return version_info return version_info
...@@ -68,7 +68,18 @@ def get_api_info(connection: CliOsduClient, config_url_key: str, url_extra_path: ...@@ -68,7 +68,18 @@ def get_api_info(connection: CliOsduClient, config_url_key: str, url_extra_path:
def version(state: State): def version(state: State):
"""Print version information to standard system out.""" """Print version information to standard system out."""
if state.is_user_friendly_mode(): if state.is_user_friendly_mode():
version_info = f"OSDU Cli Version {osducli.__VERSION__}" current_version = osducli.__VERSION__
latest_version = get_pypi_version("osducli")
version_info = f"Installed OSDU Cli Version {current_version}\n"
if latest_version is not None:
version_info += f"Latest OSDU Cli Version {latest_version}\n"
if current_version != latest_version:
version_info += f"\b\n\033[33mWARNING: You are using osdu cli version {current_version}; however version {latest_version} is available.\033[39m" # noqa: E501 pylint: disable=line-too-long
version_info += "\b\n\033[33mYou should consider upgrading via the 'pip install -U osdu-cli' command\033[39m" # noqa: E501 pylint: disable=line-too-long
else:
version_info += "Latest OSDU Cli Version - unable to check!\n"
version_info += "\n"
version_info += get_runtime_version() version_info += get_runtime_version()
print(version_info) print(version_info)
......
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import requests
from osducli.log import get_logger
logger = get_logger(__name__)
def get_pypi_version(package: str) -> str:
"""Get the latest version of a package from pypi
Args:
package (str): Package name
Returns:
str: Version number or None if unable to retrieve.
"""
try:
response = requests.get(f"https://pypi.org/pypi/{package}/json")
if response.status_code == 200:
data = response.json()
return data["info"]["version"]
except Exception as ex: # pylint: disable=broad-except
logger.debug(ex, exc_info=True)
return None
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Test cases for osducli.util.pypi"""
from unittest.mock import MagicMock, patch
from knack.testsdk.base import IntegrationTestBase
from nose2.tools import params
from osducli.util.pypi import get_pypi_version
# pylint: disable=missing-class-docstring
# pylint: disable=missing-function-docstring
# pylint: disable=too-many-public-methods
# pylint: disable=no-self-use
class TestExceptions(IntegrationTestBase):
def __init__(self, method_name):
super().__init__(None, method_name)
# region get_pypi_version
@params("name1", "name2")
def test_get_pypi_version(self, name):
"""Test valid calls succeed"""
response_mock = MagicMock()
response_mock.status_code = 200
response_mock.json.return_value = {"info": {"version": 1.0}}
with patch("requests.get", return_value=response_mock) as _:
version = get_pypi_version(name)
self.assertEqual(version, 1.0)
@params(400, 201)
def test_get_pypi_version_failure_response_code(self, response_code):
"""Test Invalid response from pypi get returns None"""
response_mock = MagicMock()
response_mock.code = response_code
with patch("requests.get", return_value=response_mock) as _:
version = get_pypi_version("osducli")
self.assertIsNone(version)
def test_exit_on_error(self):
"""Test any exception returns None"""
with patch("requests.get", side_effect=Exception()) as _:
version = get_pypi_version("osducli")
self.assertIsNone(version)
# endregion get_pypi_version
if __name__ == "__main__":
import nose2
nose2.main()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment