From 9ecafe09ac2e41a5e1859fd595328dae7bb5f306 Mon Sep 17 00:00:00 2001 From: tyler Date: Sun, 8 Nov 2026 00:00:23 -0700 Subject: [PATCH] fix to updating deleting plugins and replacing all files --- flares.py | 1 - updater.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/flares.py b/flares.py index 9e060b0..ba85483 100644 --- a/flares.py +++ b/flares.py @@ -311,7 +311,6 @@ script_dir = os.path.dirname(os.path.abspath(__file__)) os.chdir(script_dir) - # Configure logging with rotation to prevent indefinite growth if PLATFORM_NAME == 'darwin': log_path = os.path.abspath(os.path.join(os.path.dirname(sys.executable), "../../../fnirs_analysis.log")) diff --git a/updater.py b/updater.py index 15f80b8..78e1eab 100644 --- a/updater.py +++ b/updater.py @@ -462,6 +462,88 @@ def _remove_readonly(func: Callable[[str], Any], path: str, exc: Any) -> None: func(path) +def files_are_identical(src_path: str, dst_path: str) -> bool: + """ + Check if two files are identical by comparing size and modification time. + + This is much faster than hashing and catches all real changes for update purposes. + + Args: + src_path (str): Path to source file. + dst_path (str): Path to destination file. + + Returns: + bool: True if files are identical, False otherwise. + """ + try: + if not os.path.exists(src_path) or not os.path.exists(dst_path): + return False + + src_stat = os.stat(src_path) + dst_stat = os.stat(dst_path) + + # Compare size first + if src_stat.st_size != dst_stat.st_size: + return False + + # Compare modification time (rounded to 1 second to handle cross-platform differences) + src_mtime = int(src_stat.st_mtime) + dst_mtime = int(dst_stat.st_mtime) + + return src_mtime == dst_mtime + except Exception: + return False + + +def smart_replace_folder(source_dir: str, dest_dir: str) -> Tuple[int, int]: + """ + Replace files in dest_dir with files from source_dir, but only if they differ. + + Args: + source_dir (str): Source directory containing new files. + dest_dir (str): Destination directory to update. + + Returns: + Tuple[int, int]: (files_updated, files_skipped) counts. + """ + files_updated = 0 + files_skipped = 0 + + for root, dirs, files in os.walk(source_dir): + relative_path = os.path.relpath(root, source_dir) + dest_root = os.path.join(dest_dir, relative_path) + + # Create directory if it doesn't exist + if not os.path.exists(dest_root): + os.makedirs(dest_root) + + for filename in files: + src_file = os.path.join(root, filename) + dst_file = os.path.join(dest_root, filename) + + try: + # Skip if file is identical + if files_are_identical(src_file, dst_file): + files_skipped += 1 + continue + + # Remove destination if it exists (handles locked files on some systems) + if os.path.exists(dst_file): + if os.path.isdir(dst_file): + shutil.rmtree(dst_file, onexc=_remove_readonly) + else: + os.remove(dst_file) + + # Copy the file + shutil.copy2(src_file, dst_file) + files_updated += 1 + + except Exception as e: + print(f" Warning: Failed to update {filename}: {e}") + + return files_updated, files_skipped + + def finish_update_if_needed(platform_name: str, app_name: str, cfg_path: str, finish_update: bool) -> None: """ Completes a pending application update if '--finish-update' is present in the command-line arguments. @@ -565,7 +647,14 @@ def finish_update_if_needed(platform_name: str, app_name: str, cfg_path: str, fi except Exception as e: print(f"Failed to replace {app_name}_updater: {e}") - # 5. Clean up all temporary update folders (with retries for Windows file locks) + # 5. Use smart replacement - only update files that have changed + print("Using smart update: only replacing files that have changed...") + files_updated, files_skipped = smart_replace_folder(target_updater_folder, target_base) + print(f"\nUpdate summary:") + print(f" Files updated: {files_updated}") + print(f" Files skipped (unchanged): {files_skipped}") + + # 6. Clean up all temporary update folders (with retries for Windows file locks) for folder in update_folders: for attempt in range(3): try: