OTA committed for troubleshooting
This commit is contained in:
69
ota.py
69
ota.py
@@ -7,13 +7,17 @@ on the next boot rather than bricking the device.
|
|||||||
|
|
||||||
Strategy
|
Strategy
|
||||||
--------
|
--------
|
||||||
1. Fetch ota_manifest.txt from the repo to determine which files to sync.
|
1. Check if last boot was good (OK flag exists).
|
||||||
2. Compare SHA1 hashes with a local manifest (.ota_manifest.json).
|
2. If good, fetch remote commit SHA and compare with local — if unchanged,
|
||||||
3. Download only changed or missing files, writing to .tmp first.
|
skip file check entirely.
|
||||||
4. On success, rename .tmp files into place and update the manifest.
|
3. If new commit or failed boot, fetch ota_manifest.txt from the repo
|
||||||
5. If anything fails mid-update, the manifest is not updated, so the
|
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.
|
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
|
startup. If it is absent on boot, the previous update is suspected
|
||||||
bad — the manifest is wiped so all files are re-fetched cleanly.
|
bad — the manifest is wiped so all files are re-fetched cleanly.
|
||||||
|
|
||||||
@@ -146,6 +150,26 @@ def _match_pattern(name, pattern):
|
|||||||
i += 1
|
i += 1
|
||||||
return i == n and j == m
|
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():
|
def _fetch_manifest():
|
||||||
url = (
|
url = (
|
||||||
f"{GITEA_BASE}/api/v1/repos/{REPO_OWNER}/{REPO_NAME}"
|
f"{GITEA_BASE}/api/v1/repos/{REPO_OWNER}/{REPO_NAME}"
|
||||||
@@ -223,9 +247,11 @@ def _load_manifest():
|
|||||||
except Exception:
|
except Exception:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _save_manifest(manifest):
|
def _save_manifest(manifest, commit_sha=None):
|
||||||
try:
|
try:
|
||||||
with open(MANIFEST_FILE, "w") as f:
|
with open(MANIFEST_FILE, "w") as f:
|
||||||
|
if commit_sha:
|
||||||
|
manifest["_commit"] = commit_sha
|
||||||
ujson.dump(manifest, f)
|
ujson.dump(manifest, f)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
warn(f"Could not save manifest: {e}")
|
warn(f"Could not save manifest: {e}")
|
||||||
@@ -311,7 +337,7 @@ def _fetch_file_list():
|
|||||||
fetch_matching(root, manifest_patterns)
|
fetch_matching(root, manifest_patterns)
|
||||||
return files
|
return files
|
||||||
|
|
||||||
def _do_update():
|
def _do_update(commit_sha=None):
|
||||||
"""
|
"""
|
||||||
Fetch file list, download changed files, update manifest.
|
Fetch file list, download changed files, update manifest.
|
||||||
Returns True if all succeeded (or nothing needed updating).
|
Returns True if all succeeded (or nothing needed updating).
|
||||||
@@ -348,11 +374,10 @@ def _do_update():
|
|||||||
|
|
||||||
if failed:
|
if failed:
|
||||||
log_err(f"Update incomplete — {len(failed)} file(s) failed: {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, commit_sha)
|
||||||
_save_manifest(manifest)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
_save_manifest(manifest)
|
_save_manifest(manifest, commit_sha)
|
||||||
|
|
||||||
if updated:
|
if updated:
|
||||||
info(f"Update complete — {len(updated)} file(s) updated: {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
|
- If the OK flag is missing, the previous boot is assumed to have
|
||||||
failed — wipes the manifest so everything is re-fetched cleanly.
|
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.
|
- Runs the update.
|
||||||
- Clears the OK flag so main.py must re-assert it on successful start.
|
- Clears the OK flag so main.py must re-assert it on successful start.
|
||||||
"""
|
"""
|
||||||
@@ -380,17 +407,33 @@ def update():
|
|||||||
|
|
||||||
load_config()
|
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("OK flag missing — last boot may have failed")
|
||||||
warn("Wiping manifest to force full re-fetch")
|
warn("Wiping manifest to force full re-fetch")
|
||||||
_wipe_manifest()
|
_wipe_manifest()
|
||||||
|
manifest = {}
|
||||||
else:
|
else:
|
||||||
info("OK flag present — last boot was good")
|
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 the flag now; main.py must call ota.mark_ok() to re-set it
|
||||||
_clear_ok_flag()
|
_clear_ok_flag()
|
||||||
|
|
||||||
success = _do_update()
|
success = _do_update(commit_sha)
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
info("OTA check complete — booting application")
|
info("OTA check complete — booting application")
|
||||||
|
|||||||
Reference in New Issue
Block a user