""" Auto-update functionality Handles remote version checking and application updates """ import os import subprocess import re import logging from config_settings import ( AUTO_UPDATE_SERVER_HOST, AUTO_UPDATE_SERVER_USER, AUTO_UPDATE_SERVER_PASSWORD, AUTO_UPDATE_SERVER_APP_PATH, AUTO_UPDATE_SERVER_REPO_PATH, UPDATE_TIMEOUT, REPO_SYNC_TIMEOUT ) from logger_module import log_with_server def get_app_version(file_path): """ Extract version from app file Version is expected to be in the first line as: #App version X.X Args: file_path: Path to the app.py file Returns: float: Version number or None """ try: with open(file_path, 'r') as f: first_line = f.readline() if 'version' in first_line.lower(): version_match = re.search(r'version\s+(\d+\.?\d*)', first_line, re.IGNORECASE) if version_match: return float(version_match.group(1)) except Exception as e: logging.error(f"Could not determine version from {file_path}: {e}") return None def check_remote_version(hostname, device_ip): """ Check the version of the app on the remote server Returns: float: Remote version or None """ temp_dir = "/tmp/app_update" try: # Create temporary directory subprocess.run(['mkdir', '-p', temp_dir], check=True) # Download remote app.py to check version scp_command = [ 'sshpass', '-p', AUTO_UPDATE_SERVER_PASSWORD, 'scp', '-o', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null', f'{AUTO_UPDATE_SERVER_USER}@{AUTO_UPDATE_SERVER_HOST}:{AUTO_UPDATE_SERVER_APP_PATH}', f'{temp_dir}/app.py' ] result = subprocess.run(scp_command, capture_output=True, text=True, timeout=UPDATE_TIMEOUT) if result.returncode != 0: log_with_server(f"Failed to download remote app.py: {result.stderr}", hostname, device_ip) return None remote_version = get_app_version(f'{temp_dir}/app.py') log_with_server(f"Remote version: {remote_version}", hostname, device_ip) return remote_version except subprocess.TimeoutExpired: log_with_server("Connection to server timed out", hostname, device_ip) return None except Exception as e: log_with_server(f"Error checking remote version: {e}", hostname, device_ip) return None def perform_auto_update(local_app_path, local_repo_path, hostname, device_ip): """ Perform the auto-update process Args: local_app_path: Path to local app.py local_repo_path: Path to local repository hostname: Device hostname device_ip: Device IP Returns: dict with update status information """ temp_dir = "/tmp/app_update" try: # Get current local version current_version = get_app_version(local_app_path) if current_version is None: log_with_server(f"Could not determine local version", hostname, device_ip) return {"error": "Could not determine local version", "status": "failed"} # Create temporary directory subprocess.run(['mkdir', '-p', temp_dir], check=True) # Get remote version remote_version = check_remote_version(hostname, device_ip) if remote_version is None: return {"error": "Could not determine remote version", "status": "failed"} # Compare versions if remote_version <= current_version: log_with_server(f"No update needed. Current: {current_version}, Remote: {remote_version}", hostname, device_ip) return { "status": "no_update_needed", "current_version": current_version, "remote_version": remote_version, "message": "Application is already up to date" } # Download updated files log_with_server(f"Update available! Downloading version {remote_version}", hostname, device_ip) # Create backup of current app backup_path = f"{local_app_path}.backup.{current_version}" subprocess.run(['cp', local_app_path, backup_path], check=True) log_with_server(f"Backup created: {backup_path}", hostname, device_ip) # Download new app.py subprocess.run(['cp', f'{temp_dir}/app.py', local_app_path], check=True) log_with_server("New app.py downloaded successfully", hostname, device_ip) # Download repository folder repo_scp_command = [ 'sshpass', '-p', AUTO_UPDATE_SERVER_PASSWORD, 'scp', '-r', '-o', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null', f'{AUTO_UPDATE_SERVER_USER}@{AUTO_UPDATE_SERVER_HOST}:{AUTO_UPDATE_SERVER_REPO_PATH}', f'{local_repo_path}_new' ] result = subprocess.run(repo_scp_command, capture_output=True, text=True, timeout=REPO_SYNC_TIMEOUT) if result.returncode == 0: subprocess.run(['rm', '-rf', local_repo_path], check=True) subprocess.run(['mv', f'{local_repo_path}_new', local_repo_path], check=True) log_with_server("Repository updated successfully", hostname, device_ip) else: log_with_server(f"Repository update failed: {result.stderr}", hostname, device_ip) log_with_server("Update completed successfully. Scheduling restart...", hostname, device_ip) # Schedule device restart restart_script = '''#!/bin/bash sleep 3 sudo reboot ''' with open('/tmp/restart_device.sh', 'w') as f: f.write(restart_script) subprocess.run(['chmod', '+x', '/tmp/restart_device.sh'], check=True) subprocess.Popen(['/tmp/restart_device.sh'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) return { "status": "success", "message": f"Updated from version {current_version} to {remote_version}. Device restarting...", "old_version": current_version, "new_version": remote_version, "restart_scheduled": True } except Exception as e: log_with_server(f"Auto-update error: {str(e)}", hostname, device_ip) return {"error": f"Auto-update failed: {str(e)}", "status": "failed"} finally: # Cleanup temp directory try: subprocess.run(['rm', '-rf', temp_dir], check=True) except: pass