feat: update Windows installer to include WixUtilExtension and close running application
chore: bump version to 0.6.2 fix: improve update manager logic for caching and fetching releases
This commit is contained in:
parent
cbd1f3f77c
commit
88d9f200ab
9 changed files with 2947 additions and 2917 deletions
|
|
@ -44,10 +44,8 @@ class UpdateManager:
|
|||
self.current_version = current_version
|
||||
self.forgejo_url = "https://git.him-tools.de"
|
||||
self.repo = "HIM-public/webdrop-bridge"
|
||||
self.api_endpoint = (
|
||||
f"{self.forgejo_url}/api/v1/repos/{self.repo}/releases/latest"
|
||||
)
|
||||
|
||||
self.api_endpoint = f"{self.forgejo_url}/api/v1/repos/{self.repo}/releases/latest"
|
||||
|
||||
# Cache management
|
||||
self.cache_dir = config_dir or Path.home() / ".webdrop-bridge"
|
||||
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
|
@ -68,7 +66,7 @@ class UpdateManager:
|
|||
"""
|
||||
# Remove 'v' prefix if present
|
||||
version_str = version_str.lstrip("v")
|
||||
|
||||
|
||||
try:
|
||||
parts = version_str.split(".")
|
||||
if len(parts) != 3:
|
||||
|
|
@ -146,44 +144,45 @@ class UpdateManager:
|
|||
Release object if newer version available, None otherwise
|
||||
"""
|
||||
logger.debug(f"check_for_updates() called, current version: {self.current_version}")
|
||||
|
||||
# Try cache first
|
||||
logger.debug("Checking cache...")
|
||||
|
||||
# Only use cache when a pending update was already found (avoids
|
||||
# showing the update dialog on every start). "No update" is never
|
||||
# cached so that a freshly published release is visible immediately.
|
||||
logger.debug("Checking cache for pending update...")
|
||||
cached = self._load_cache()
|
||||
if cached:
|
||||
logger.debug("Found cached release")
|
||||
release_data = cached.get("release")
|
||||
if release_data:
|
||||
version = release_data["tag_name"].lstrip("v")
|
||||
if not self._is_newer_version(version):
|
||||
logger.info("No newer version available (cached)")
|
||||
return None
|
||||
return Release(**release_data)
|
||||
logger.debug(f"Cached pending update version: {version}")
|
||||
if self._is_newer_version(version):
|
||||
logger.info(f"Returning cached pending update: {version}")
|
||||
return Release(**release_data)
|
||||
else:
|
||||
# Current version is >= cached release (e.g. already updated)
|
||||
logger.debug("Cached release is no longer newer — discarding cache")
|
||||
self.cache_file.unlink(missing_ok=True)
|
||||
|
||||
# Fetch from API
|
||||
# Always fetch fresh from API so new releases are seen immediately
|
||||
logger.debug("Fetching from API...")
|
||||
try:
|
||||
logger.info(f"Checking for updates from {self.api_endpoint}")
|
||||
|
||||
# Run in thread pool with aggressive timeout
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
response = await asyncio.wait_for(
|
||||
loop.run_in_executor(
|
||||
None, self._fetch_release
|
||||
),
|
||||
timeout=8 # Timeout after network call also has timeout
|
||||
loop.run_in_executor(None, self._fetch_release),
|
||||
timeout=8,
|
||||
)
|
||||
|
||||
|
||||
if not response:
|
||||
return None
|
||||
|
||||
# Check if newer version
|
||||
version = response["tag_name"].lstrip("v")
|
||||
if not self._is_newer_version(version):
|
||||
logger.info(f"Latest version {version} is not newer than {self.current_version}")
|
||||
self._save_cache(response)
|
||||
return None
|
||||
|
||||
# Cache the found update so repeated starts don't hammer the API
|
||||
logger.info(f"New version available: {version}")
|
||||
release = Release(**response)
|
||||
self._save_cache(response)
|
||||
|
|
@ -204,11 +203,11 @@ class UpdateManager:
|
|||
"""
|
||||
try:
|
||||
logger.debug(f"Fetching release from {self.api_endpoint}")
|
||||
|
||||
|
||||
# Set socket timeout to prevent hanging
|
||||
old_timeout = socket.getdefaulttimeout()
|
||||
socket.setdefaulttimeout(5)
|
||||
|
||||
|
||||
try:
|
||||
logger.debug("Opening URL connection...")
|
||||
with urlopen(self.api_endpoint, timeout=5) as response:
|
||||
|
|
@ -227,13 +226,14 @@ class UpdateManager:
|
|||
}
|
||||
finally:
|
||||
socket.setdefaulttimeout(old_timeout)
|
||||
|
||||
|
||||
except socket.timeout as e:
|
||||
logger.error(f"Socket timeout (5s) connecting to {self.api_endpoint}")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch release: {type(e).__name__}: {e}")
|
||||
import traceback
|
||||
|
||||
logger.debug(traceback.format_exc())
|
||||
return None
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ class UpdateManager:
|
|||
|
||||
try:
|
||||
logger.info(f"Downloading {installer_asset['name']}")
|
||||
|
||||
|
||||
# Run in thread pool with 5-minute timeout for large files
|
||||
loop = asyncio.get_event_loop()
|
||||
success = await asyncio.wait_for(
|
||||
|
|
@ -280,9 +280,9 @@ class UpdateManager:
|
|||
installer_asset["browser_download_url"],
|
||||
output_file,
|
||||
),
|
||||
timeout=300
|
||||
timeout=300,
|
||||
)
|
||||
|
||||
|
||||
if success:
|
||||
logger.info(f"Downloaded to {output_file}")
|
||||
return output_file
|
||||
|
|
@ -320,9 +320,7 @@ class UpdateManager:
|
|||
logger.error(f"Download failed: {e}")
|
||||
return False
|
||||
|
||||
async def verify_checksum(
|
||||
self, file_path: Path, release: Release
|
||||
) -> bool:
|
||||
async def verify_checksum(self, file_path: Path, release: Release) -> bool:
|
||||
"""Verify file checksum against release checksum file.
|
||||
|
||||
Args:
|
||||
|
|
@ -345,7 +343,7 @@ class UpdateManager:
|
|||
|
||||
try:
|
||||
logger.info("Verifying checksum...")
|
||||
|
||||
|
||||
# Download checksum file with 30 second timeout
|
||||
loop = asyncio.get_event_loop()
|
||||
checksum_content = await asyncio.wait_for(
|
||||
|
|
@ -354,7 +352,7 @@ class UpdateManager:
|
|||
self._download_checksum,
|
||||
checksum_asset["browser_download_url"],
|
||||
),
|
||||
timeout=30
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
if not checksum_content:
|
||||
|
|
@ -374,9 +372,7 @@ class UpdateManager:
|
|||
logger.info("Checksum verification passed")
|
||||
return True
|
||||
else:
|
||||
logger.error(
|
||||
f"Checksum mismatch: {file_checksum} != {expected_checksum}"
|
||||
)
|
||||
logger.error(f"Checksum mismatch: {file_checksum} != {expected_checksum}")
|
||||
return False
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue