OTA committed for troubleshooting

This commit is contained in:
2026-04-06 12:19:23 +02:00
parent 1907e97049
commit 53f3b026c6

69
ota.py
View File

@@ -7,13 +7,17 @@ on the next boot rather than bricking the device.
Strategy
--------
1. Fetch ota_manifest.txt from the repo to determine which files to sync.
2. Compare SHA1 hashes with a local manifest (.ota_manifest.json).
3. Download only changed or missing files, writing to .tmp first.
4. On success, rename .tmp files into place and update the manifest.
5. If anything fails mid-update, the manifest is not updated, so the
1. Check if last boot was good (OK flag exists).
2. If good, fetch remote commit SHA and compare with local — if unchanged,
skip file check entirely.
3. If new commit or failed boot, fetch ota_manifest.txt from the repo
to determine which files to sync.
4. Compare SHA1 hashes with a local manifest (.ota_manifest.json).
5. Download only changed or missing files, writing to .tmp first.
6. On success, rename .tmp files into place and update the manifest.
7. If anything fails mid-update, the manifest is not updated, so the
next boot will retry. Partially written .tmp files are cleaned up.
6. A "safety" flag file (.ota_ok) is written by main.py on successful
8. A "safety" flag file (.ota_ok) is written by main.py on successful
startup. If it is absent on boot, the previous update is suspected
bad — the manifest is wiped so all files are re-fetched cleanly.
@@ -146,6 +150,26 @@ def _match_pattern(name, pattern):
i += 1
return i == n and j == m
def _fetch_commit_sha():
url = (
f"{GITEA_BASE}/api/v1/repos/{REPO_OWNER}/{REPO_NAME}"
f"/branches/{REPO_BRANCH}"
)
info(f"Fetching commit from {url}")
try:
r = urequests.get(url, headers=_headers())
info(f"Response status: {r.status_code}")
if r.status_code == 200:
data = r.json()
r.close()
sha = data.get("commit", {}).get("sha")
info(f"Got commit: {sha}")
return sha
r.close()
except Exception as e:
log_err(f"Failed to fetch commit: {e}")
return None
def _fetch_manifest():
url = (
f"{GITEA_BASE}/api/v1/repos/{REPO_OWNER}/{REPO_NAME}"
@@ -223,9 +247,11 @@ def _load_manifest():
except Exception:
return {}
def _save_manifest(manifest):
def _save_manifest(manifest, commit_sha=None):
try:
with open(MANIFEST_FILE, "w") as f:
if commit_sha:
manifest["_commit"] = commit_sha
ujson.dump(manifest, f)
except Exception as e:
warn(f"Could not save manifest: {e}")
@@ -311,7 +337,7 @@ def _fetch_file_list():
fetch_matching(root, manifest_patterns)
return files
def _do_update():
def _do_update(commit_sha=None):
"""
Fetch file list, download changed files, update manifest.
Returns True if all succeeded (or nothing needed updating).
@@ -348,11 +374,10 @@ def _do_update():
if failed:
log_err(f"Update incomplete — {len(failed)} file(s) failed: {failed}")
# Save partial manifest so successful files aren't re-downloaded
_save_manifest(manifest)
_save_manifest(manifest, commit_sha)
return False
_save_manifest(manifest)
_save_manifest(manifest, commit_sha)
if updated:
info(f"Update complete — {len(updated)} file(s) updated: {updated}")
@@ -371,6 +396,8 @@ def update():
- If the OK flag is missing, the previous boot is assumed to have
failed — wipes the manifest so everything is re-fetched cleanly.
- If the commit hash hasn't changed and last boot was good, skip
file comparison entirely.
- Runs the update.
- Clears the OK flag so main.py must re-assert it on successful start.
"""
@@ -380,17 +407,33 @@ def update():
load_config()
if not _ok_flag_exists():
ok_flag = _ok_flag_exists()
manifest = _load_manifest()
if not ok_flag:
warn("OK flag missing — last boot may have failed")
warn("Wiping manifest to force full re-fetch")
_wipe_manifest()
manifest = {}
else:
info("OK flag present — last boot was good")
commit_sha = _fetch_commit_sha()
if ok_flag and commit_sha and manifest.get("_commit") == commit_sha:
info(f"Commit unchanged ({commit_sha[:8]}) — skipping file check")
info("-" * 40)
return
if commit_sha:
info(f"Remote commit: {commit_sha[:8]}")
else:
warn("Could not fetch remote commit — proceeding with file check")
# Clear the flag now; main.py must call ota.mark_ok() to re-set it
_clear_ok_flag()
success = _do_update()
success = _do_update(commit_sha)
if success:
info("OTA check complete — booting application")