From f4b3be015a39bbe2a1ad107a2ec467b6bb18d821 Mon Sep 17 00:00:00 2001 From: Richard Taylor Date: Wed, 18 Feb 2026 17:29:52 +0000 Subject: [PATCH] Clean up existing install before extracting .mpk install_mpk() would fail with EEXIST if the destination folder already existed from a previous (possibly partial) install, or if a dangling symlink occupied the path (e.g. a dev symlink whose target was removed). Before extracting, now remove the destination if it is: - A real directory (via shutil.rmtree) - A regular file (via os.remove) - A symlink, including broken ones (via os.remove as a fallback) This enables clean reinstalls, updates, and recovery from failed installs. Co-Authored-By: Claude Opus 4.6 --- .../lib/mpos/content/app_manager.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/internal_filesystem/lib/mpos/content/app_manager.py b/internal_filesystem/lib/mpos/content/app_manager.py index b8385b04..07ef1b86 100644 --- a/internal_filesystem/lib/mpos/content/app_manager.py +++ b/internal_filesystem/lib/mpos/content/app_manager.py @@ -157,6 +157,25 @@ def uninstall_app(app_fullname): @staticmethod def install_mpk(temp_zip_path, dest_folder): try: + # Step 1: Remove any existing (possibly partial) install or symlink + try: + st = os.stat(dest_folder) + if st[0] & 0x4000: # It's a real directory + import shutil + shutil.rmtree(dest_folder) + print("Removed existing folder:", dest_folder) + else: + os.remove(dest_folder) + print("Removed existing file:", dest_folder) + except OSError: + pass # Doesn't exist, that's fine + # Also remove if it's a symlink (broken or otherwise) + try: + os.remove(dest_folder) + print("Removed symlink:", dest_folder) + except OSError: + pass # Not a symlink or already removed + # Step 2: Unzip the file print("Unzipping it to:", dest_folder) with zipfile.ZipFile(temp_zip_path, "r") as zip_ref: