Updated for auto update client wnen a new version is on the server
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#App version 2.9 - Added configuration mode for "notconfig" devices
|
||||
#App version 3.0 - Auto-update from server + notconfig/TestTemplate workflow
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
@@ -9,6 +9,9 @@ import grp
|
||||
from dependency_utils import install_package_from_wheel, check_and_install_dependencies
|
||||
|
||||
|
||||
# Application version (compared against server release to decide whether to auto-update)
|
||||
APP_VERSION = "3.0"
|
||||
|
||||
# Global configuration mode flag
|
||||
CONFIGURATION_MODE = False
|
||||
|
||||
@@ -396,10 +399,85 @@ if TEMPLATE_IMAGE:
|
||||
print("Clone and configure this image before deploying.")
|
||||
print("=" * 60)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Auto-update from server release
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _parse_version(v: str):
|
||||
"""Convert '3.0' or '2.10' to a comparable tuple of ints."""
|
||||
try:
|
||||
return tuple(int(x) for x in str(v).strip().split('.'))
|
||||
except Exception:
|
||||
return (0,)
|
||||
|
||||
|
||||
def check_and_apply_update():
|
||||
"""
|
||||
Called once at startup (after config sync, before heavy init).
|
||||
1. Ask the server for the latest available client version.
|
||||
2. If the server version is newer, download the zip, extract app.py,
|
||||
replace the running script, and restart the process.
|
||||
Template images (TEMPLATE_IMAGE=True) are never updated this way.
|
||||
"""
|
||||
import urllib.parse as _up
|
||||
import zipfile as _zf
|
||||
import tempfile as _tf
|
||||
try:
|
||||
parsed = _up.urlparse(APP_CONFIG.get("server_log_url", "http://rpi-ansible.sibiusb.harting.intra:80/logs"))
|
||||
server_base = f"{parsed.scheme}://{parsed.netloc}"
|
||||
|
||||
import requests as _req
|
||||
resp = _req.get(f"{server_base}/api/wmt/client/version", timeout=5)
|
||||
if resp.status_code != 200:
|
||||
print(f"Update check: server returned {resp.status_code} – skipping.")
|
||||
return
|
||||
|
||||
meta = resp.json()
|
||||
server_version = meta.get("version", "0")
|
||||
if _parse_version(server_version) <= _parse_version(APP_VERSION):
|
||||
print(f"✅ App is up to date (v{APP_VERSION}).")
|
||||
return
|
||||
|
||||
print(f"🔄 New version available: v{server_version} (current: v{APP_VERSION}). Downloading...")
|
||||
dl_resp = _req.get(f"{server_base}/api/wmt/client/download", timeout=30, stream=True)
|
||||
dl_resp.raise_for_status()
|
||||
|
||||
# Write zip to a temp file
|
||||
with _tf.NamedTemporaryFile(suffix='.zip', delete=False) as tmp_zip:
|
||||
for chunk in dl_resp.iter_content(chunk_size=65536):
|
||||
tmp_zip.write(chunk)
|
||||
tmp_zip_path = tmp_zip.name
|
||||
|
||||
# Extract ONLY app.py from the zip – config, data, and every other
|
||||
# file on the device are intentionally left untouched.
|
||||
# We never call zf.extractall() to avoid accidentally overwriting anything.
|
||||
with _zf.ZipFile(tmp_zip_path, 'r') as zf:
|
||||
if 'app.py' not in zf.namelist():
|
||||
print("❌ Update zip does not contain app.py – aborting.")
|
||||
os.unlink(tmp_zip_path)
|
||||
return
|
||||
with _tf.NamedTemporaryFile(suffix='.py', delete=False, dir='.') as tmp_app:
|
||||
tmp_app.write(zf.read('app.py')) # read only 'app.py' by name
|
||||
tmp_app_path = tmp_app.name
|
||||
|
||||
os.unlink(tmp_zip_path)
|
||||
|
||||
# Atomically replace app.py and restart
|
||||
current_script = os.path.abspath(__file__)
|
||||
os.replace(tmp_app_path, current_script)
|
||||
print(f"✅ Updated to v{server_version}. Restarting...")
|
||||
os.execv(sys.executable, [sys.executable] + sys.argv)
|
||||
# execv replaces the process – code below is never reached
|
||||
|
||||
except Exception as e:
|
||||
print(f"Update check failed (non-fatal): {e}")
|
||||
|
||||
|
||||
# Attempt to sync with server at startup (non-blocking – failures are logged and ignored)
|
||||
# Skipped for the base template image.
|
||||
if not TEMPLATE_IMAGE:
|
||||
sync_config_with_server()
|
||||
check_and_apply_update()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Card presence flag – controls whether RFID functions are started
|
||||
|
||||
Reference in New Issue
Block a user