Skip to content

Commit

Permalink
chore: save distribution method and prompt for update (#426)
Browse files Browse the repository at this point in the history
* feat: save distribution channel and prompt for update

* test: fix snapshot tests

* fix: PR reviews

* fix: security issue

* test: fixing test snapshots

* chore: updating the builds

* chore: tweaks

* chore: refactor version prompt logic

* fix: PR reviews

* test: adding test for each scenario

* fix: PR reviews

* fix: simplify pyinstaller add files-PR reviews

* fix: pyinstaller command for adding file

---------

Co-authored-by: Neil Campbell <neil.campbell@makerx.com.au>
  • Loading branch information
negar-abbasi and neilcampbell authored Mar 1, 2024
1 parent 643ab01 commit baa0f9f
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 9 deletions.
6 changes: 2 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,8 @@ docs_generate = "sphinx-build -b markdown -E docs/sphinx docs/cli"
docs_toc = "gfm-toc docs/cli/index.md -e 3"
docs_title = {shell = "(echo \"# AlgoKit CLI Reference Documentation\\n\\n\"; cat docs/cli/index.md) > docs/cli/temp.md && mv docs/cli/temp.md docs/cli/index.md"}
docs = ["docs_generate", "docs_toc", "docs_title"]
package_unix = "pyinstaller --clean --onedir --hidden-import jinja2_ansible_filters --hidden-import multiformats_config --copy-metadata algokit --name algokit --noconfirm src/algokit/__main__.py --add-data './misc/multiformats_config/multibase-table.json:multiformats_config/' --add-data './misc/multiformats_config/multicodec-table.json:multiformats_config/'"
package_windows = "pyinstaller --clean --onedir --hidden-import jinja2_ansible_filters --hidden-import multiformats_config --copy-metadata algokit --name algokit --noconfirm src/algokit/__main__.py --add-data ./misc/multiformats_config/multibase-table.json;multiformats_config/ --add-data ./misc/multiformats_config/multicodec-table.json;multiformats_config/"


package_unix = "pyinstaller --clean --onedir --hidden-import jinja2_ansible_filters --hidden-import multiformats_config --copy-metadata algokit --name algokit --noconfirm src/algokit/__main__.py --add-data './misc/multiformats_config:multiformats_config/' --add-data './src/algokit/resources:algokit/resources/'"
package_windows = "pyinstaller --clean --onedir --hidden-import jinja2_ansible_filters --hidden-import multiformats_config --copy-metadata algokit --name algokit --noconfirm src/algokit/__main__.py --add-data ./misc/multiformats_config;multiformats_config/ --add-data ./src/algokit/resources;algokit/resources/"
[tool.ruff]
line-length = 120
select = [
Expand Down
8 changes: 8 additions & 0 deletions src/algokit/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,11 @@ def get_base_python_path() -> str | None:
return str(candidate_path)
# give up, we tried...
return this_python


def is_binary_mode() -> bool:
"""
Check if the current Python interpreter is running in a native binary frozen environment.
return: True if running in a native binary frozen environment, False otherwise.
"""
return getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
44 changes: 42 additions & 2 deletions src/algokit/core/version_prompt.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import importlib.resources as importlib_resources
import logging
import re
from datetime import timedelta
Expand All @@ -6,13 +7,23 @@
import click
import httpx

from algokit import __name__ as algokit_name
from algokit.core.conf import get_app_config_dir, get_app_state_dir, get_current_package_version
from algokit.core.utils import is_binary_mode

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"
DISTRIBUTION_METHOD_UPDATE_COMMAND = {
"snap": "`snap refresh algokit`",
"winget": "`winget upgrade algokit`",
"brew": "`brew upgrade algokit`",
}
UNKNOWN_DISTRIBUTION_METHOD_UPDATE_INSTRUCTION = "the tool used to install AlgoKit"
# TODO: Set this version as part of releasing the binary distributions.
BINARY_DISTRIBUTION_RELEASE_VERSION = "99.99.99"


def do_version_prompt() -> None:
Expand All @@ -26,8 +37,26 @@ def do_version_prompt() -> None:
logger.debug("Could not determine latest version")
return

if _get_version_sequence(current_version) < _get_version_sequence(latest_version):
logger.info(f"You are using AlgoKit version {current_version}, however version {latest_version} is available.")
current_version_sequence = _get_version_sequence(current_version)
if current_version_sequence < _get_version_sequence(latest_version):
update_instruction = UNKNOWN_DISTRIBUTION_METHOD_UPDATE_INSTRUCTION
if is_binary_mode():
distribution = _get_distribution_method()
update_instruction = (
DISTRIBUTION_METHOD_UPDATE_COMMAND.get(distribution, UNKNOWN_DISTRIBUTION_METHOD_UPDATE_INSTRUCTION)
if distribution
else UNKNOWN_DISTRIBUTION_METHOD_UPDATE_INSTRUCTION
)
# If you're not using the binary mode, then you've used pipx to install AlgoKit.
# One exception is that older versions of the brew package used pipx,
# however require updating via brew, so we show the default update instruction instead.
elif current_version_sequence >= _get_version_sequence(BINARY_DISTRIBUTION_RELEASE_VERSION):
update_instruction = "`pipx upgrade algokit`"

logger.info(
f"You are using AlgoKit version {current_version}, however version {latest_version} is available. "
f"Please update using {update_instruction}."
)
else:
logger.debug("Current version is up to date")

Expand Down Expand Up @@ -85,6 +114,17 @@ def _skip_version_prompt() -> bool:
return disable_marker.exists()


def _get_distribution_method() -> str | None:
file_path = importlib_resources.files(algokit_name) / "resources" / "distribution-method"
with file_path.open("r", encoding="utf-8", errors="strict") as file:
content = file.read().strip()

if content in ["snap", "winget", "brew"]:
return content
else:
return None


skip_version_check_option = click.option(
"--skip-version-check",
is_flag=True,
Expand Down
Empty file.
23 changes: 23 additions & 0 deletions tests/version_check/test_version_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,26 @@ def test_version_check_enable_version_check(app_dir_mock: AppDirs) -> None:
assert result.exit_code == 0
assert not disable_version_check_path.exists()
verify(result.output, scrubber=make_scrubber(app_dir_mock))


@pytest.mark.parametrize(
("method", "message"),
[
("snap", "snap refresh algokit"),
("brew", "brew upgrade algokit"),
("winget", "winget upgrade algokit"),
(None, "the tool used to install AlgoKit"),
],
)
def test_version_prompt_according_to_distribution_method(
mocker: MockerFixture, app_dir_mock: AppDirs, method: str, message: str
) -> None:
mocker.patch("algokit.core.version_prompt._get_distribution_method").return_value = method
mocker.patch("algokit.core.version_prompt.is_binary_mode").return_value = True
mocker.patch("algokit.core.version_prompt.get_current_package_version").return_value = "1.0.0"
version_cache = app_dir_mock.app_state_dir / "last-version-check"
version_cache.write_text("2.0.0", encoding="utf-8")

result = invoke("bootstrap env", skip_version_check=False)
assert result.exit_code == 0
assert message in result.output
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
DEBUG: 1234.56.78 found in cache {app_state}/last-version-check
DEBUG: HTTP Request: GET https://api.github.com/repos/algorandfoundation/algokit-cli/releases/latest "HTTP/1.1 200 OK"
DEBUG: Latest version tag: v{new_version}
You are using AlgoKit version {current_version}, however version {new_version} is available.
You are using AlgoKit version {current_version}, however version {new_version} is available. Please update using the tool used to install AlgoKit.
DEBUG: Attempting to load project config from {current_working_directory}/.algokit.toml
DEBUG: No .algokit.toml file found in the project directory.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
DEBUG: {app_state}/last-version-check inaccessible
DEBUG: HTTP Request: GET https://api.github.com/repos/algorandfoundation/algokit-cli/releases/latest "HTTP/1.1 200 OK"
DEBUG: Latest version tag: v{new_version}
You are using AlgoKit version {current_version}, however version {new_version} is available.
You are using AlgoKit version {current_version}, however version {new_version} is available. Please update using the tool used to install AlgoKit.
DEBUG: Attempting to load project config from {current_working_directory}/.algokit.toml
DEBUG: No .algokit.toml file found in the project directory.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
DEBUG: 1234.56.78 found in cache {app_state}/last-version-check
You are using AlgoKit version {current_version}, however version 1234.56.78 is available.
You are using AlgoKit version {current_version}, however version 1234.56.78 is available. Please update using the tool used to install AlgoKit.
DEBUG: Attempting to load project config from {current_working_directory}/.algokit.toml
DEBUG: No .algokit.toml file found in the project directory.

1 comment on commit baa0f9f

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/algokit
   __init__.py15753%6–13, 17–24, 32–34
   __main__.py440%1–7
src/algokit/cli
   completions.py108298%83, 98
   deploy.py72790%44, 46, 92–94, 158, 182
   dispenser.py121199%77
   doctor.py48394%142–144
   explore.py501276%34–39, 41–46
   generate.py67396%74–75, 140
   goal.py44198%71
   init.py2752591%384–385, 440, 443–445, 456, 460, 516, 542, 571, 604, 613–615, 618–623, 636, 653, 665–666, 683–686
   localnet.py1191587%74–78, 111, 123, 138–148, 161, 206, 227–228
   task.py34391%25–28
src/algokit/cli/common
   utils.py26292%120, 123
src/algokit/cli/tasks
   analyze.py81199%81
   assets.py821384%65–66, 72, 74–75, 105, 119, 125–126, 132, 134, 136–137
   ipfs.py51884%52, 80, 92, 94–95, 105–107
   mint.py66494%48, 70, 91, 250
   send_transaction.py651085%52–53, 57, 89, 158, 170–174
   sign_transaction.py59886%21, 28–30, 71–72, 109, 123
   transfer.py39392%26, 90, 117
   utils.py994555%26–34, 40–43, 75–76, 100–101, 125–133, 152–162, 209, 258–259, 279–290, 297–299
   vanity_address.py561082%41, 45–48, 112, 114, 121–123
   wallet.py79495%21, 66, 136, 162
src/algokit/core
   bootstrap.py1191191%42, 106–107, 129, 156, 185–190
   conf.py661577%12, 24, 28, 36, 38, 72–74, 92–100
   deploy.py691184%61–64, 73–75, 79, 84, 91–93
   dispenser.py2022687%91, 123–124, 141–149, 191–192, 198–200, 218–219, 259–260, 318, 332–334, 345–346, 356, 369, 384
   doctor.py65789%67–69, 92–94, 134
   generate.py48394%44, 81, 99
   goal.py60395%30–31, 41
   init.py39685%59, 63–68, 76
   log_handlers.py68790%50–51, 63, 112–116, 125
   proc.py45198%98
   sandbox.py2181892%62, 73–75, 96, 142–149, 160, 457, 473, 498, 506
   typed_client_generation.py80594%55–57, 70, 75
   utils.py1063171%44–45, 49–68, 129, 132, 138–152
   version_prompt.py921485%37–38, 68, 87–90, 108, 118–125, 148
src/algokit/core/tasks
   analyze.py93397%105–112, 187
   ipfs.py63789%58–64, 140, 144, 146, 152
   nfd.py491373%25, 31, 34–41, 70–72, 99–101
   vanity_address.py903462%49–50, 54, 59–75, 92–108, 128–131
   wallet.py71593%37, 129, 155–157
src/algokit/core/tasks/mint
   mint.py781087%123–133, 187
   models.py901188%50, 52, 57, 71–74, 85–88
TOTAL363143288% 

Tests Skipped Failures Errors Time
411 0 💤 0 ❌ 0 🔥 23.079s ⏱️

Please sign in to comment.