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:
claudi 2026-02-25 14:08:41 +01:00
parent cbd1f3f77c
commit 88d9f200ab
9 changed files with 2947 additions and 2917 deletions

View file

@ -2,7 +2,7 @@
# Application # Application
APP_NAME=WebDrop Bridge APP_NAME=WebDrop Bridge
APP_VERSION=0.6.0 APP_VERSION=0.6.2
# Web App # Web App
WEBAPP_URL=file:///./webapp/index.html WEBAPP_URL=file:///./webapp/index.html

View file

@ -1,3 +1,19 @@
## [0.6.2] - 2026-02-25
### Added
### Changed
### Fixed
## [0.6.1] - 2026-02-25
### Added
### Changed
### Fixed
## [0.6.0] - 2026-02-20 ## [0.6.0] - 2026-02-20
### Added ### Added

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:ui="http://schemas.microsoft.com/wix/2010/ui"> xmlns:ui="http://schemas.microsoft.com/wix/2010/ui"
<Product Id="*" Name="WebDrop Bridge" Language="1033" Version="0.6.0" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Id="*" Name="WebDrop Bridge" Language="1033" Version="0.6.1"
Manufacturer="HIM-Tools" Manufacturer="HIM-Tools"
UpgradeCode="12345678-1234-1234-1234-123456789012"> UpgradeCode="12345678-1234-1234-1234-123456789012">
@ -23,6 +24,13 @@
<UIRef Id="WixUI_InstallDir" /> <UIRef Id="WixUI_InstallDir" />
<UIRef Id="WixUI_ErrorProgressText" /> <UIRef Id="WixUI_ErrorProgressText" />
<!-- Close running application before installation -->
<util:CloseApplication
Target="WebDropBridge.exe"
CloseMessage="yes"
RebootPrompt="no"
ElevatedCloseMessage="no" />
<Feature Id="ProductFeature" Title="WebDrop Bridge" Level="1"> <Feature Id="ProductFeature" Title="WebDrop Bridge" Level="1">
<ComponentGroupRef Id="AppFiles" /> <ComponentGroupRef Id="AppFiles" />
<ComponentRef Id="ProgramMenuShortcut" /> <ComponentRef Id="ProgramMenuShortcut" />

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -299,6 +299,7 @@ class WindowsBuilder:
candle_cmd = [ candle_cmd = [
str(candle_exe), str(candle_exe),
"-ext", "WixUIExtension", "-ext", "WixUIExtension",
"-ext", "WixUtilExtension",
f"-dDistDir={self.dist_dir}", f"-dDistDir={self.dist_dir}",
f"-dSourceDir={self.dist_dir}\\WebDropBridge", # Set SourceDir for Heat-generated files f"-dSourceDir={self.dist_dir}\\WebDropBridge", # Set SourceDir for Heat-generated files
f"-dResourcesDir={self.project_root}\\resources", # Set ResourcesDir for branding assets f"-dResourcesDir={self.project_root}\\resources", # Set ResourcesDir for branding assets
@ -319,6 +320,7 @@ class WindowsBuilder:
light_cmd = [ light_cmd = [
str(light_exe), str(light_exe),
"-ext", "WixUIExtension", "-ext", "WixUIExtension",
"-ext", "WixUtilExtension",
"-b", str(self.dist_dir / "WebDropBridge"), # Base path for source files "-b", str(self.dist_dir / "WebDropBridge"), # Base path for source files
"-o", str(msi_output), "-o", str(msi_output),
str(wix_obj), str(wix_obj),
@ -355,7 +357,8 @@ class WindowsBuilder:
""" """
wix_content = f'''<?xml version="1.0" encoding="UTF-8"?> wix_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:ui="http://schemas.microsoft.com/wix/2010/ui"> xmlns:ui="http://schemas.microsoft.com/wix/2010/ui"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Id="*" Name="WebDrop Bridge" Language="1033" Version="{self.version}" <Product Id="*" Name="WebDrop Bridge" Language="1033" Version="{self.version}"
Manufacturer="HIM-Tools" Manufacturer="HIM-Tools"
UpgradeCode="12345678-1234-1234-1234-123456789012"> UpgradeCode="12345678-1234-1234-1234-123456789012">
@ -378,6 +381,13 @@ class WindowsBuilder:
<UIRef Id="WixUI_InstallDir" /> <UIRef Id="WixUI_InstallDir" />
<UIRef Id="WixUI_ErrorProgressText" /> <UIRef Id="WixUI_ErrorProgressText" />
<!-- Close running application before installation -->
<util:CloseApplication
Target="WebDropBridge.exe"
CloseMessage="yes"
RebootPrompt="no"
ElevatedCloseMessage="no" />
<Feature Id="ProductFeature" Title="WebDrop Bridge" Level="1"> <Feature Id="ProductFeature" Title="WebDrop Bridge" Level="1">
<ComponentGroupRef Id="AppFiles" /> <ComponentGroupRef Id="AppFiles" />
<ComponentRef Id="ProgramMenuShortcut" /> <ComponentRef Id="ProgramMenuShortcut" />

View file

@ -1,6 +1,6 @@
"""WebDrop Bridge - Qt-based desktop application for intelligent drag-and-drop file handling.""" """WebDrop Bridge - Qt-based desktop application for intelligent drag-and-drop file handling."""
__version__ = "0.6.0" __version__ = "0.6.2"
__author__ = "WebDrop Team" __author__ = "WebDrop Team"
__license__ = "MIT" __license__ = "MIT"

View file

@ -44,9 +44,7 @@ class UpdateManager:
self.current_version = current_version self.current_version = current_version
self.forgejo_url = "https://git.him-tools.de" self.forgejo_url = "https://git.him-tools.de"
self.repo = "HIM-public/webdrop-bridge" self.repo = "HIM-public/webdrop-bridge"
self.api_endpoint = ( self.api_endpoint = f"{self.forgejo_url}/api/v1/repos/{self.repo}/releases/latest"
f"{self.forgejo_url}/api/v1/repos/{self.repo}/releases/latest"
)
# Cache management # Cache management
self.cache_dir = config_dir or Path.home() / ".webdrop-bridge" self.cache_dir = config_dir or Path.home() / ".webdrop-bridge"
@ -147,43 +145,44 @@ class UpdateManager:
""" """
logger.debug(f"check_for_updates() called, current version: {self.current_version}") logger.debug(f"check_for_updates() called, current version: {self.current_version}")
# Try cache first # Only use cache when a pending update was already found (avoids
logger.debug("Checking cache...") # 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() cached = self._load_cache()
if cached: if cached:
logger.debug("Found cached release")
release_data = cached.get("release") release_data = cached.get("release")
if release_data: if release_data:
version = release_data["tag_name"].lstrip("v") version = release_data["tag_name"].lstrip("v")
if not self._is_newer_version(version): logger.debug(f"Cached pending update version: {version}")
logger.info("No newer version available (cached)") if self._is_newer_version(version):
return None logger.info(f"Returning cached pending update: {version}")
return Release(**release_data) 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...") logger.debug("Fetching from API...")
try: try:
logger.info(f"Checking for updates from {self.api_endpoint}") logger.info(f"Checking for updates from {self.api_endpoint}")
# Run in thread pool with aggressive timeout
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
response = await asyncio.wait_for( response = await asyncio.wait_for(
loop.run_in_executor( loop.run_in_executor(None, self._fetch_release),
None, self._fetch_release timeout=8,
),
timeout=8 # Timeout after network call also has timeout
) )
if not response: if not response:
return None return None
# Check if newer version
version = response["tag_name"].lstrip("v") version = response["tag_name"].lstrip("v")
if not self._is_newer_version(version): if not self._is_newer_version(version):
logger.info(f"Latest version {version} is not newer than {self.current_version}") logger.info(f"Latest version {version} is not newer than {self.current_version}")
self._save_cache(response)
return None return None
# Cache the found update so repeated starts don't hammer the API
logger.info(f"New version available: {version}") logger.info(f"New version available: {version}")
release = Release(**response) release = Release(**response)
self._save_cache(response) self._save_cache(response)
@ -234,6 +233,7 @@ class UpdateManager:
except Exception as e: except Exception as e:
logger.error(f"Failed to fetch release: {type(e).__name__}: {e}") logger.error(f"Failed to fetch release: {type(e).__name__}: {e}")
import traceback import traceback
logger.debug(traceback.format_exc()) logger.debug(traceback.format_exc())
return None return None
@ -280,7 +280,7 @@ class UpdateManager:
installer_asset["browser_download_url"], installer_asset["browser_download_url"],
output_file, output_file,
), ),
timeout=300 timeout=300,
) )
if success: if success:
@ -320,9 +320,7 @@ class UpdateManager:
logger.error(f"Download failed: {e}") logger.error(f"Download failed: {e}")
return False return False
async def verify_checksum( async def verify_checksum(self, file_path: Path, release: Release) -> bool:
self, file_path: Path, release: Release
) -> bool:
"""Verify file checksum against release checksum file. """Verify file checksum against release checksum file.
Args: Args:
@ -354,7 +352,7 @@ class UpdateManager:
self._download_checksum, self._download_checksum,
checksum_asset["browser_download_url"], checksum_asset["browser_download_url"],
), ),
timeout=30 timeout=30,
) )
if not checksum_content: if not checksum_content:
@ -374,9 +372,7 @@ class UpdateManager:
logger.info("Checksum verification passed") logger.info("Checksum verification passed")
return True return True
else: else:
logger.error( logger.error(f"Checksum mismatch: {file_checksum} != {expected_checksum}")
f"Checksum mismatch: {file_checksum} != {expected_checksum}"
)
return False return False
except asyncio.TimeoutError: except asyncio.TimeoutError: