-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(version-check): Added check to periodically check for new releas…
…es on GitHub and inform when found (#111) * feat(version-check): add version check functionality * test: fix enable/disable version check * review: address review feedback * refactor: move version prompt enable/disable to a config subcommand, similar to git * feat(version-check): add latest algokit version to doctor command * refactor(version-check): remove version parsing, sorry time travelers 🖖 * lint: update to latest ruff version 🐩 * lint: exclude dist/ folder from mypy checks (if present) * test(version-check): additional tests Co-authored-by: Adam Chidlow <achidlow@users.noreply.github.com>
- Loading branch information
1 parent
1eb7335
commit 1772439
Showing
43 changed files
with
370 additions
and
48 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import click | ||
|
||
from algokit.core.version_prompt import version_prompt_configuration_command | ||
|
||
|
||
@click.group("config", short_help="Configure AlgoKit options") | ||
def config_group() -> None: | ||
pass | ||
|
||
|
||
config_group.add_command(version_prompt_configuration_command) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import logging | ||
|
||
import click | ||
|
||
from algokit.core import proc | ||
|
||
logger = logging.getLogger(__name__) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import logging | ||
import os | ||
import re | ||
from datetime import timedelta | ||
from time import time | ||
|
||
import click | ||
import httpx | ||
|
||
from algokit.core.conf import get_app_config_dir, get_app_state_dir, get_current_package_version | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
LATEST_URL = "https://api.github.com/repos/algorandfoundation/algokit-cli/releases/latest" | ||
VERSION_CHECK_INTERVAL = timedelta(weeks=1).total_seconds() | ||
DISABLE_CHECK_MARKER = "disable-version-prompt" | ||
|
||
|
||
def do_version_prompt() -> None: | ||
if _skip_version_prompt(): | ||
logger.debug("Version prompt disabled") | ||
return | ||
|
||
current_version = get_current_package_version() | ||
latest_version = get_latest_version_or_cached() | ||
if latest_version is None: | ||
logger.debug("Could not determine latest version") | ||
return | ||
|
||
if current_version != latest_version: | ||
logger.info(f"You are using AlgoKit version {current_version}, however version {latest_version} is available.") | ||
else: | ||
logger.debug("Current version is up to date") | ||
|
||
|
||
def get_latest_version_or_cached() -> str | None: | ||
version_check_path = get_app_state_dir() / "last-version-check" | ||
|
||
try: | ||
last_checked = os.path.getmtime(version_check_path) | ||
version = version_check_path.read_text(encoding="utf-8") | ||
except IOError: | ||
logger.debug(f"{version_check_path} inaccessible") | ||
last_checked = 0 | ||
version = None | ||
else: | ||
logger.debug(f"{version} found in cache {version_check_path}") | ||
|
||
if (time() - last_checked) > VERSION_CHECK_INTERVAL: | ||
try: | ||
version = get_latest_github_version() | ||
except Exception as ex: | ||
logger.debug("Checking for latest version failed", exc_info=ex) | ||
# update last checked time even if check failed | ||
version_check_path.touch() | ||
else: | ||
version_check_path.write_text(version, encoding="utf-8") | ||
# handle case where the first check failed, so we have an empty file | ||
return version or None | ||
|
||
|
||
def get_latest_github_version() -> str: | ||
headers = {"ACCEPT": "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28"} | ||
# TODO: remove GH_TOKEN auth once algokit repo is public | ||
if gh_token := os.getenv("GH_TOKEN"): | ||
headers["Authorization"] = f"Bearer {gh_token}" | ||
|
||
response = httpx.get(LATEST_URL, headers=headers) | ||
response.raise_for_status() | ||
|
||
json = response.json() | ||
tag_name = json["tag_name"] | ||
logger.debug(f"Latest version tag: {tag_name}") | ||
match = re.match(r"v(\d+\.\d+\.\d+)", tag_name) | ||
if not match: | ||
raise ValueError(f"Unable to extract version from tag_name: {tag_name}") | ||
return match.group(1) | ||
|
||
|
||
def _skip_version_prompt() -> bool: | ||
disable_marker = get_app_config_dir() / DISABLE_CHECK_MARKER | ||
return disable_marker.exists() | ||
|
||
|
||
skip_version_check_option = click.option( | ||
"--skip-version-check", | ||
is_flag=True, | ||
show_default=False, | ||
default=False, | ||
help="Skip version checking and prompting.", | ||
) | ||
|
||
|
||
@click.command("version-prompt", short_help="Enables or disables version prompt") | ||
@click.argument("enable", required=False, type=bool, default=None) | ||
def version_prompt_configuration_command(*, enable: bool | None) -> None: | ||
if enable is None: | ||
logger.info(str(not _skip_version_prompt())) | ||
else: | ||
disable_marker = get_app_config_dir() / DISABLE_CHECK_MARKER | ||
if enable: | ||
disable_marker.unlink(missing_ok=True) | ||
logger.info("📡 Resuming check for new versions") | ||
else: | ||
disable_marker.touch() | ||
logger.info("🚫 Will stop checking for new versions") |
Oops, something went wrong.
1772439
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coverage Report