diff --git a/rendercv/cli.py b/rendercv/cli.py index e9155cc..b213372 100644 --- a/rendercv/cli.py +++ b/rendercv/cli.py @@ -6,6 +6,7 @@ output. """ import json +import urllib.request import pathlib from typing import Annotated, Callable, Optional import re @@ -42,8 +43,57 @@ app = typer.Typer( ) +def get_latest_version_number_from_pypi() -> Optional[str]: + """Get the latest version number of RenderCV from PyPI. + + Example: + ```python + get_latest_version_number_from_pypi() + ``` + will return: + `#!python "1.1"` + + Returns: + Optional[str]: The latest version number of RenderCV from PyPI. Returns None if + the version number cannot be fetched. + """ + version = None + url = "https://pypi.org/pypi/rendercv/json" + try: + with urllib.request.urlopen(url) as response: + data = response.read() + encoding = response.info().get_content_charset("utf-8") + json_data = json.loads(data.decode(encoding)) + version = json_data["info"]["version"] + except Exception: + pass + + return version + + +def warn_if_new_version_is_available() -> bool: + """Check if a new version of RenderCV is available and print a warning message if + there is a new version. Also, return True if there is a new version, and False + otherwise. + + Returns: + bool: True if there is a new version, and False otherwise. + """ + latest_version = get_latest_version_number_from_pypi() + if latest_version is not None and __version__ != latest_version: + warning( + f"A new version of RenderCV is available! You are using v{__version__}," + f" and the latest version is v{latest_version}." + ) + return True + else: + return False + + def welcome(): """Print a welcome message to the terminal.""" + warn_if_new_version_is_available() + table = rich.table.Table( title=( "\nWelcome to [bold]Render[dodger_blue3]CV[/dodger_blue3][/bold]! Some" @@ -867,4 +917,6 @@ def main( ] = None, ): if version_requested: - information(f"RenderCV v{__version__}") + there_is_a_new_version = warn_if_new_version_is_available() + if not there_is_a_new_version: + print(f"RenderCV v{__version__}") diff --git a/tests/test_cli.py b/tests/test_cli.py index b7d4606..10a5486 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -11,6 +11,7 @@ import typer.testing import rendercv.cli as cli import rendercv.data_models as dm +from rendercv import __version__ def run_render_command(input_file_path, working_path, extra_arguments=[]): @@ -509,3 +510,46 @@ def test_create_theme_command_theme_already_exists(tmp_path): def test_main_file(): subprocess.run([sys.executable, "-m", "rendercv", "--help"], check=True) + + +def test_get_latest_version_number_from_pypi(): + version = cli.get_latest_version_number_from_pypi() + assert isinstance(version, str) + + +def test_if_welcome_prints_new_version_available(monkeypatch): + monkeypatch.setattr(cli, "get_latest_version_number_from_pypi", lambda: "99999") + import io + import contextlib + + with contextlib.redirect_stdout(io.StringIO()) as f: + cli.welcome() + output = f.getvalue() + + assert "A new version of RenderCV is available!" in output + + +def test_rendercv_version_when_there_is_a_new_version(monkeypatch): + monkeypatch.setattr(cli, "get_latest_version_number_from_pypi", lambda: "99999") + + result = runner.invoke(cli.app, ["--version"]) + + assert "A new version of RenderCV is available!" in result.stdout + + +def test_rendercv_version_when_there_is_not_a_new_version(monkeypatch): + monkeypatch.setattr(cli, "get_latest_version_number_from_pypi", lambda: __version__) + + result = runner.invoke(cli.app, ["--version"]) + + assert __version__ in result.stdout + + +def test_warn_if_new_version_is_available(monkeypatch): + monkeypatch.setattr(cli, "get_latest_version_number_from_pypi", lambda: __version__) + + assert not cli.warn_if_new_version_is_available() + + monkeypatch.setattr(cli, "get_latest_version_number_from_pypi", lambda: "999") + + assert cli.warn_if_new_version_is_available()