Implement support for incremental client updates

This commit is contained in:
codeking 2025-03-15 18:42:10 +01:00
parent d33a73bb96
commit b744ed0a2d
4 changed files with 73 additions and 4 deletions

View file

@ -8,6 +8,9 @@ class Constants:
SP_API_BASE_URL: Final[str] = 'https://api.simplifiedprivacy.is/api/v1' SP_API_BASE_URL: Final[str] = 'https://api.simplifiedprivacy.is/api/v1'
HV_CLIENT_PATH: Final[str] = os.environ.get('HV_CLIENT_PATH')
HV_CLIENT_VERSION_NUMBER: Final[str] = os.environ.get('HV_CLIENT_VERSION_NUMBER')
HOME: Final[str] = os.path.expanduser('~') HOME: Final[str] = os.path.expanduser('~')
SYSTEM_CONFIG_PATH: Final[str] = '/etc' SYSTEM_CONFIG_PATH: Final[str] = '/etc'

View file

@ -6,6 +6,10 @@ class CommandNotFoundError(OSError):
super().__init__(f"Command '{subject}' could not be found.") super().__init__(f"Command '{subject}' could not be found.")
class UnknownClientPathError(Exception):
pass
class UnknownClientVersionError(Exception): class UnknownClientVersionError(Exception):
pass pass

View file

@ -1,4 +1,5 @@
from core.Errors import UnknownClientVersionError from core.Constants import Constants
from core.Errors import UnknownClientPathError, UnknownClientVersionError, CommandNotFoundError
from core.controllers.ApplicationController import ApplicationController from core.controllers.ApplicationController import ApplicationController
from core.controllers.ApplicationVersionController import ApplicationVersionController from core.controllers.ApplicationVersionController import ApplicationVersionController
from core.controllers.ClientVersionController import ClientVersionController from core.controllers.ClientVersionController import ClientVersionController
@ -10,6 +11,9 @@ from core.observers.ConnectionObserver import ConnectionObserver
from typing import Optional from typing import Optional
import os import os
import pathlib import pathlib
import re
import shutil
import subprocess
class ClientController: class ClientController:
@ -21,9 +25,7 @@ class ClientController:
@staticmethod @staticmethod
def get_version(): def get_version():
version_number = os.environ.get('HV_VERSION_NUMBER') if (version_number := Constants.HV_CLIENT_VERSION_NUMBER) is None:
if version_number is None:
raise UnknownClientVersionError('The client version could not be determined.') raise UnknownClientVersionError('The client version could not be determined.')
return ClientVersionController.get_or_new(version_number) return ClientVersionController.get_or_new(version_number)
@ -44,6 +46,20 @@ class ClientController:
from core.controllers.ConnectionController import ConnectionController from core.controllers.ConnectionController import ConnectionController
ConnectionController.with_preferred_connection(task=ClientController.__sync, client_observer=client_observer, connection_observer=connection_observer) ConnectionController.with_preferred_connection(task=ClientController.__sync, client_observer=client_observer, connection_observer=connection_observer)
@staticmethod
def update(client_observer: ClientObserver = None, connection_observer: ConnectionObserver = None):
from core.controllers.ConnectionController import ConnectionController
ConnectionController.with_preferred_connection(task=ClientController.__update, client_observer=client_observer, connection_observer=connection_observer)
@staticmethod
def __get_path():
if (path := Constants.HV_CLIENT_PATH) is None:
raise UnknownClientPathError('The client path could not be determined.')
return path
@staticmethod @staticmethod
def __sync(client_observer: Optional[ClientObserver] = None, proxies: Optional[dict] = None): def __sync(client_observer: Optional[ClientObserver] = None, proxies: Optional[dict] = None):
@ -62,3 +78,44 @@ class ClientController:
SubscriptionPlanController._sync(proxies=proxies) SubscriptionPlanController._sync(proxies=proxies)
ConfigurationController.update_last_synced_at() ConfigurationController.update_last_synced_at()
if client_observer is not None:
client_observer.notify('synchronized')
@staticmethod
def __update(client_observer: Optional[ClientObserver] = None, proxies: Optional[dict] = None):
if ClientController.can_be_updated():
if client_observer is not None:
client_observer.notify('updating')
client_path = ClientController.__get_path()
update_environment = os.environ.copy()
if proxies is not None:
update_environment | dict(http_proxy=proxies['http'], https_proxy=proxies['https'])
if shutil.which('hv-updater') is None:
raise CommandNotFoundError('hv-updater')
process = subprocess.Popen(('hv-updater', '--overwrite', '--remove-old', client_path), env=update_environment, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
highest_reported_progress = 0
for line in iter(process.stdout.readline, ''):
match = re.search(r'(\d+\.\d+)%', line)
if match and (progress := float(match.group(1))) > highest_reported_progress:
highest_reported_progress = progress
if client_observer is not None:
client_observer.notify('update_progressing', None, dict(
progress=progress
))
if client_observer is not None:
client_observer.notify('updated')

View file

@ -4,4 +4,9 @@ from core.observers.BaseObserver import BaseObserver
class ClientObserver(BaseObserver): class ClientObserver(BaseObserver):
def __init__(self): def __init__(self):
self.on_synchronizing = [] self.on_synchronizing = []
self.on_synchronized = []
self.on_updating = []
self.on_update_progressing = []
self.on_updated = []