-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(commands): Add update command (#75)
* add update command
- Loading branch information
1 parent
93cfd42
commit 2e3e906
Showing
11 changed files
with
408 additions
and
94 deletions.
There are no files selected for viewing
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,14 @@ | ||
from __future__ import annotations | ||
|
||
import json | ||
from urllib.request import urlopen | ||
|
||
|
||
def check_for_update(current_version: str) -> str | None: | ||
url = "https://api.github.com/repos/getsentry/devservices/releases/latest" | ||
with urlopen(url) as response: | ||
if response.status == 200: | ||
data = json.loads(response.read().decode("utf-8")) | ||
latest_version = str(data["tag_name"]) | ||
return latest_version | ||
return None |
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,66 @@ | ||
from __future__ import annotations | ||
|
||
import platform | ||
import sys | ||
from argparse import _SubParsersAction | ||
from argparse import ArgumentParser | ||
from argparse import Namespace | ||
from importlib import metadata | ||
|
||
from devservices.commands.check_for_update import check_for_update | ||
from devservices.constants import DEVSERVICES_DOWNLOAD_URL | ||
from devservices.exceptions import BinaryInstallError | ||
from devservices.exceptions import DevservicesUpdateError | ||
from devservices.utils.install_binary import install_binary | ||
|
||
|
||
def is_in_virtualenv() -> bool: | ||
return hasattr(sys, "real_prefix") or ( | ||
hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix | ||
) | ||
|
||
|
||
def update_version(exec_path: str, latest_version: str) -> None: | ||
system = platform.system().lower() | ||
url = f"{DEVSERVICES_DOWNLOAD_URL}/{latest_version}/devservices-{system}" | ||
try: | ||
install_binary("devservices", exec_path, latest_version, url) | ||
except BinaryInstallError as e: | ||
raise DevservicesUpdateError(f"Failed to update devservices: {e}") | ||
|
||
print(f"Devservices {latest_version} updated successfully") | ||
|
||
|
||
def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None: | ||
parser = subparsers.add_parser( | ||
"update", help="update devservices to the latest version" | ||
) | ||
parser.set_defaults(func=update) | ||
|
||
|
||
def update(args: Namespace) -> None: | ||
current_version = metadata.version("devservices") | ||
latest_version = check_for_update(current_version) | ||
|
||
if latest_version is None: | ||
raise DevservicesUpdateError("Failed to check for updates.") | ||
|
||
if latest_version == current_version: | ||
print("You're already on the latest version.") | ||
return | ||
|
||
print(f"A new version of devservices is available: {latest_version}") | ||
|
||
if is_in_virtualenv(): | ||
print("You are running in a virtual environment.") | ||
print( | ||
"To update, please update your requirements.txt or requirements-dev.txt file with the new version." | ||
) | ||
print( | ||
f"For example, update the line in requirements.txt to: devservices=={latest_version}" | ||
) | ||
print("Then, run: pip install --update -r requirements.txt") | ||
return | ||
|
||
print("Upgrading to the latest version...") | ||
update_version(sys.executable, latest_version) |
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,52 @@ | ||
from __future__ import annotations | ||
|
||
import os | ||
import shutil | ||
import tempfile | ||
import time | ||
from urllib.request import urlretrieve | ||
|
||
from devservices.constants import BINARY_PERMISSIONS | ||
from devservices.exceptions import BinaryInstallError | ||
|
||
|
||
def install_binary( | ||
binary_name: str, | ||
exec_path: str, | ||
version: str, | ||
url: str, | ||
) -> None: | ||
with tempfile.TemporaryDirectory() as temp_dir: | ||
temp_file = os.path.join(temp_dir, binary_name) | ||
|
||
# Download the binary with retries | ||
max_retries = 3 | ||
retry_delay_seconds = 1 | ||
print(f"Downloading {binary_name} {version} from {url}...") | ||
for attempt in range(max_retries): | ||
try: | ||
urlretrieve(url, temp_file) | ||
break | ||
except Exception as e: | ||
if attempt < max_retries - 1: | ||
print( | ||
f"Download failed. Retrying in {retry_delay_seconds} seconds... (Attempt {attempt + 1}/{max_retries - 1})" | ||
) | ||
time.sleep(retry_delay_seconds) | ||
else: | ||
raise BinaryInstallError( | ||
f"Failed to download {binary_name} after {max_retries} attempts: {e}" | ||
) | ||
|
||
# Make the binary executable | ||
try: | ||
os.chmod(temp_file, BINARY_PERMISSIONS) | ||
except (PermissionError, FileNotFoundError) as e: | ||
raise BinaryInstallError(f"Failed to set executable permissions: {e}") | ||
|
||
try: | ||
shutil.move(temp_file, exec_path) | ||
except (PermissionError, FileNotFoundError) as e: | ||
raise BinaryInstallError( | ||
f"Failed to move {binary_name} binary to {exec_path}: {e}" | ||
) |
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,4 +1,5 @@ | ||
--index-url https://pypi.devinfra.sentry.io/simple | ||
pyyaml==6.0.1 | ||
packaging==24.0 | ||
sentry-devenv==1.8.0 | ||
sentry-sdk==2.14.0 |
Oops, something went wrong.