Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 79 additions & 2 deletions ardupilot_methodic_configurator/backend_internet.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""

import os
import shutil
import subprocess
import sys
import tempfile
Expand All @@ -18,17 +19,19 @@
from logging import error as logging_error
from logging import info as logging_info
from logging import shutdown as logging_shutdown
from pathlib import Path
from typing import Any, Callable, Optional
from urllib.parse import urljoin
from webbrowser import open as webbrowser_open

from platformdirs import user_config_dir
from requests import HTTPError as requests_HTTPError
from requests import RequestException as requests_RequestException
from requests import Timeout as requests_Timeout
from requests import get as requests_get
from requests.exceptions import RequestException

from ardupilot_methodic_configurator import _
from ardupilot_methodic_configurator import _, __version__

# Constants
GITHUB_API_URL_RELEASES = "https://api.github.com/repos/ArduPilot/MethodicConfigurator/releases/"
Expand Down Expand Up @@ -146,6 +149,58 @@
raise


def create_backup(progress_callback: Optional[Callable[[float, str], None]] = None) -> bool:
"""
Backup AMC installation and Vehicles folder.

Returns:
True on success, False on any error.

"""
try:
version = __version__
config_dir = Path(user_config_dir(".ardupilot_methodic_configurator", appauthor=False, roaming=True))
backups_dir = config_dir / "backups" / version
backups_dir.mkdir(parents=True, exist_ok=True)

# Backup Vehicles folder
vehicles_dir = config_dir / "vehicles"
if vehicles_dir.exists():
shutil.copytree(vehicles_dir, backups_dir / "vehicles", dirs_exist_ok=True)
logging_info(_("Vehicles folder backed up to %s"), backups_dir / "Vehicles")
else:
logging_info(_("No Vehicles folder found to backup."))

# Backup AMC wheel
try:
subprocess.run(

Check failure on line 176 in ardupilot_methodic_configurator/backend_internet.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (S603)

ardupilot_methodic_configurator/backend_internet.py:176:13: S603 `subprocess` call: check for execution of untrusted input
[
sys.executable,
"-m",
"pip",
"download",
f"ardupilot_methodic_configurator=={version}",
"-d",
str(backups_dir),
],
check=True,
)
logging_info(_("AMC wheel backup complete at %s"), backups_dir)

except subprocess.CalledProcessError as e:
logging_error(_("Failed to backup AMC wheel: %s"), e)
return False

if progress_callback:
progress_callback(100.0, _("Backup complete"))

return True

except (PermissionError, OSError) as e:
logging_error(_("Backup failed: %s"), e)
return False


def download_and_install_on_windows(
download_url: str,
file_name: str,
Expand All @@ -172,6 +227,9 @@

"""
logging_info(_("Downloading and installing new version for Windows..."))

create_backup(progress_callback)

try:
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = os.path.join(temp_dir, file_name)
Expand Down Expand Up @@ -237,10 +295,29 @@
logging_info(_("Updating via pip for Linux and macOS..."))

if progress_callback:
progress_callback(0.0, _("Backing up current version..."))

backup_ok = create_backup(progress_callback)

if not backup_ok:
logging_error(_("Backup failed. Aborting update."))
if progress_callback:
progress_callback(0.0, _("Backup failed. Update aborted."))
return False

if progress_callback:
progress_callback(100.0, _("Backup complete"))
progress_callback(0.0, _("Starting installation..."))

ret = subprocess.check_call( # noqa: S603
[sys.executable, "-m", "pip", "install", "--upgrade", "ardupilot_methodic_configurator"]
[
sys.executable,
"-m",
"pip",
"install",
"--upgrade",
"ardupilot_methodic_configurator",
]
)

if ret == 0 and progress_callback:
Expand Down
Loading