diff --git a/app/cli.py b/app/cli.py index 941e25c..7eafd3f 100644 --- a/app/cli.py +++ b/app/cli.py @@ -23,6 +23,9 @@ def invoke(self, ctx: click.Context) -> None: CONTEXT_SETTINGS = {"max_content_width": 120} +# Bound the release check so the CLI cannot hang on an unresponsive network +LATEST_RELEASE_TIMEOUT_SECONDS = 5 + @click.group( cls=LoggingGroup, @@ -39,14 +42,28 @@ def cli(ctx: click.Context, verbose: bool) -> None: current_version = Version.parse_version_string(__version__) ctx.obj[CliContextKey.VERSION] = current_version - latest_version = ( - requests.get( - "https://github.com/git-mastery/app/releases/latest", allow_redirects=False + + latest_version = None + try: + response = requests.get( + "https://github.com/git-mastery/app/releases/latest", + allow_redirects=False, + timeout=LATEST_RELEASE_TIMEOUT_SECONDS, ) - .headers["Location"] - .rsplit("/", 1)[-1] - ) - if current_version.is_behind(Version.parse_version_string(latest_version)): + # GitHub redirects to the tag of the latest release; without the redirect + # there is no version to compare against + location = response.headers.get("Location") + if location is None: + warn( + "Unable to verify the latest version release: no redirect to the latest " + f"release tag (status {response.status_code})" + ) + else: + latest_version = Version.parse_version_string(location.rsplit("/", 1)[-1]) + except (requests.exceptions.RequestException, ValueError) as e: + warn(f"Unable to verify the latest version release: {e}") + + if latest_version is not None and current_version.is_behind(latest_version): warn( click.style( f"Your version of Git-Mastery app {current_version} is behind the latest version {latest_version}.",