rack:固件下载目录自动清理(每小时,FwStore.prune)
- 固件库里登记过的(界面上传)不自动删;没登记的(ota_push.py 直传、台架镜像)放满 7 天删; 上传中断的 .tmp 1 小时删;非 <16 hex>.bin / .tmp 的文件名一律不碰 - 板子下载一次几秒就完、回滚用的是板上另一个槽,删旧文件不影响任何板子 - main.py:有 --fw-dir 时起清理循环,出错只记日志;test_firmware.py 加 2 例,共 41 例(本机 + 服务器) - 已部署;本机模拟板实测放一个 8 天前的孤儿镜像,启动即被清掉 Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -204,3 +204,43 @@ def test_ota_events_are_named_by_arg(env):
|
||||
ev = {e["arg"]: e for e in c.get("/api/events").json() if e["type"] == 18}
|
||||
assert ev[3]["name"] == "固件升级(失败):哈希不对" and ev[3]["severe"] is True
|
||||
assert ev[5]["name"] == "固件升级(回滚)" and ev[5]["severe"] is True
|
||||
|
||||
|
||||
# ---- 自动清理 -------------------------------------------------------------------
|
||||
|
||||
def test_prune_removes_old_orphans_and_stale_tmp_only(tmp_path):
|
||||
from prrack.firmware import ORPHAN_KEEP_S, TMP_KEEP_S
|
||||
db = Db(str(tmp_path / "a.db"))
|
||||
d = tmp_path / "fw"
|
||||
fw = FwStore(db, str(d), BASE)
|
||||
lib = fw.add(image(), "phonerail.bin", "zhouyang") # 界面上传的:登记在库里
|
||||
now = time.time()
|
||||
|
||||
def put(name, age_s):
|
||||
f = d / name
|
||||
f.write_bytes(b"x")
|
||||
os.utime(f, (now - age_s, now - age_s))
|
||||
return name
|
||||
|
||||
old_orphan = put("0123456789abcdef.bin", ORPHAN_KEEP_S + 60) # ota_push.py 传的,放了 7 天多
|
||||
new_orphan = put("fedcba9876543210.bin", ORPHAN_KEEP_S - 60) # 还没到 7 天
|
||||
old_tmp = put(".0123456789abcdef.bin.tmp", TMP_KEEP_S + 60) # 上传中断留下的
|
||||
new_tmp = put(".fedcba9876543210.bin.tmp", 60) # 可能正在传
|
||||
other = put("README.txt", ORPHAN_KEEP_S * 10) # 不是本模块的名字:不碰
|
||||
os.utime(d / (lib["sha256"][:16] + ".bin"), (now - ORPHAN_KEEP_S * 10,) * 2) # 库里的再老也不删
|
||||
|
||||
gone = fw.prune(now)
|
||||
assert sorted(gone) == sorted([old_orphan, old_tmp])
|
||||
left = set(os.listdir(d))
|
||||
assert {new_orphan, new_tmp, other, lib["sha256"][:16] + ".bin"} <= left
|
||||
assert old_orphan not in left and old_tmp not in left
|
||||
assert fw.get(lib["sha256"])["present"] is True
|
||||
assert fw.prune(now) == [] # 幂等
|
||||
|
||||
|
||||
def test_prune_after_library_delete_is_a_no_op(tmp_path):
|
||||
db = Db(str(tmp_path / "a.db"))
|
||||
fw = FwStore(db, str(tmp_path / "fw"), BASE)
|
||||
sha = fw.add(image(), "a.bin", "u")["sha256"]
|
||||
fw.delete(sha) # 界面删除:文件与登记一起没了
|
||||
assert fw.prune() == []
|
||||
|
||||
在新工单中引用
屏蔽一个用户