functional connectivity, pylance, and other improvements

This commit is contained in:
2026-08-22 23:38:13 -07:00
parent 19bd3f1279
commit e37275a1bb
14 changed files with 1455 additions and 841 deletions
+45 -26
View File
@@ -1,6 +1,7 @@
"""
Filename: flares_updater.py
Description: FLARES updater executable
Note: Compliant with pylance strict type checking
Author: Tyler de Zeeuw
License: GPL-3.0
@@ -15,8 +16,11 @@ import psutil
import shutil
import platform
import subprocess
from typing import Union
from pathlib import Path
from datetime import datetime
PLATFORM_NAME = platform.system().lower()
APP_NAME = "flares"
@@ -27,13 +31,14 @@ else:
LOG_FILE = _log_path
def log(msg):
def log(msg: str) -> None:
with open(LOG_FILE, "a", encoding="utf-8") as f:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
f.write(f"{timestamp} - {msg}\n")
def kill_all_processes_by_executable(exe_path):
def kill_all_processes_by_executable(exe_path: Union[str, Path]) -> bool:
terminated_any = False
exe_path = os.path.realpath(exe_path)
@@ -65,7 +70,7 @@ def kill_all_processes_by_executable(exe_path):
return terminated_any
def _terminate_process(proc):
def _terminate_process(proc: psutil.Process) -> None:
try:
proc.terminate()
proc.wait(timeout=10)
@@ -77,7 +82,7 @@ def _terminate_process(proc):
log(f"Process {proc.pid} killed.")
def wait_for_unlock(path, timeout=100):
def wait_for_unlock(path: Union[str, Path], timeout: Union[int, float] = 100) -> None:
start_time = time.time()
while time.time() - start_time < timeout:
try:
@@ -93,7 +98,7 @@ def wait_for_unlock(path, timeout=100):
log(f"Failed to delete after wait: {path}")
def delete_path(path):
def delete_path(path: Union[str, Path]) -> None:
if os.path.exists(path):
try:
if os.path.isdir(path):
@@ -106,7 +111,7 @@ def delete_path(path):
log(f"Error deleting {path}: {e}")
def copy_update_files(src_folder, dest_folder, updater_name):
def copy_update_files(src_folder: Union[str, Path], dest_folder: Union[str, Path], updater_name: str) -> None:
for item in os.listdir(src_folder):
if item.lower() == updater_name.lower():
log(f"Skipping updater executable: {item}")
@@ -125,7 +130,7 @@ def copy_update_files(src_folder, dest_folder, updater_name):
log(f"Error copying {s} -> {d}: {e}")
def copy_update_files_darwin(src_folder, dest_folder, updater_name):
def copy_update_files_darwin(src_folder: Union[str, Path], dest_folder: Union[str, Path], updater_name: str) -> None:
updater_name = updater_name + ".app"
@@ -147,19 +152,33 @@ def copy_update_files_darwin(src_folder, dest_folder, updater_name):
log(f"Error copying {s} -> {d}: {e}")
def remove_quarantine(app_path):
def remove_quarantine(app_path: Union[str, Path]) -> bool:
"""Removes the macOS quarantine extended attribute from an application bundle using osascript.
Returns True on success, False on error or cancellation.
"""
clean_path: str = str(app_path)
escaped_path: str = shlex.quote(clean_path)
script = f'''
do shell script "xattr -d -r com.apple.quarantine {shlex.quote(app_path)}" with administrator privileges with prompt "{APP_NAME} needs privileges to finish the update. (1/2)"
do shell script "xattr -d -r com.apple.quarantine {escaped_path}" with administrator privileges with prompt "{APP_NAME} needs privileges to finish the update. (1/2)"
'''
try:
subprocess.run(['osascript', '-e', script], check=True)
subprocess.run(["osascript", "-e", script], check=True)
print("✅ Quarantine attribute removed.")
return True
except subprocess.CalledProcessError as e:
print("❌ Failed to remove quarantine attribute.")
print(e)
return False
def main():
main_exe: str = ""
app_dir: Path = Path()
bundle_dir: Path = Path()
parent_bundle_dir: Path = Path()
try:
log(f"[Updater] sys.argv: {sys.argv}")
@@ -171,10 +190,10 @@ def main():
main_exe = sys.argv[2]
# Interesting naming convention
parent_dir = os.path.dirname(os.path.abspath(main_exe))
pparent_dir = os.path.dirname(parent_dir)
ppparent_dir = os.path.dirname(pparent_dir)
pppparent_dir = os.path.dirname(ppparent_dir)
main_exe_path = Path(main_exe).resolve()
app_dir = main_exe_path.parent
bundle_dir = main_exe_path.parents[2]
parent_bundle_dir = main_exe_path.parents[3]
updater_name = os.path.basename(sys.argv[0])
@@ -183,13 +202,13 @@ def main():
log(f"Main EXE: {main_exe}")
log(f"Updater EXE: {updater_name}")
if PLATFORM_NAME == 'darwin':
log(f"Main App Folder: {ppparent_dir}")
log(f"Main App Folder: {bundle_dir}")
# Kill all instances of main app
kill_all_processes_by_executable(main_exe)
# Wait until main_exe process is fully gone (polling)
for _ in range(20): # wait max 10 seconds
for _ in range(10): # wait max 10 seconds
running = False
for proc in psutil.process_iter(['exe', 'cmdline']):
try:
@@ -215,17 +234,17 @@ def main():
# Delete old version files
if PLATFORM_NAME == 'darwin':
log(f'Attempting to delete {ppparent_dir}')
delete_path(ppparent_dir)
log(f'Attempting to delete {bundle_dir}')
delete_path(str(bundle_dir))
update_folder = os.path.join(sys.argv[1], f"{APP_NAME}-darwin")
copy_update_files_darwin(update_folder, pppparent_dir, updater_name)
copy_update_files_darwin(update_folder, str(parent_bundle_dir), updater_name)
else:
delete_path(main_exe)
wait_for_unlock(os.path.join(parent_dir, "_internal"))
wait_for_unlock(os.path.join(str(app_dir), "_internal"))
# Copy new files excluding the updater itself
copy_update_files(update_folder, parent_dir, updater_name)
copy_update_files(update_folder, str(app_dir), updater_name)
except Exception as e:
log(f"Something went wrong: {e}")
@@ -237,13 +256,13 @@ def main():
log("Added executable bit")
if PLATFORM_NAME == 'darwin':
os.chmod(ppparent_dir, 0o755)
os.chmod(str(bundle_dir), 0o755)
log("Added executable bit")
remove_quarantine(ppparent_dir)
log(f"Removed the quarantine flag on {ppparent_dir}")
subprocess.Popen(['open', ppparent_dir, "--args", "--finish-update"])
remove_quarantine(str(bundle_dir))
log(f"Removed the quarantine flag on {bundle_dir}")
subprocess.Popen(['open', str(bundle_dir), "--args", "--finish-update"])
else:
subprocess.Popen([main_exe, "--finish-update"], cwd=parent_dir)
subprocess.Popen([main_exe, "--finish-update"], cwd=str(app_dir))
log("Relaunched main app.")
except Exception as e: