feat: 自动隐藏被覆盖的打包历史

This commit is contained in:
shenlei
2026-07-24 12:05:37 +09:00
parent 4dccc4d96b
commit bb8a0afeac
9 changed files with 111 additions and 4 deletions
+1
View File
@@ -24,6 +24,7 @@ def _migrate_db():
migrations = [
("tasks", "branch", "VARCHAR DEFAULT 'main'"),
("tasks", "error_category", "VARCHAR"),
("tasks", "superseded_at", "TIMESTAMP"),
]
with engine.connect() as conn:
for table, column, col_type in migrations:
+2
View File
@@ -25,6 +25,8 @@ class Task(Base):
created_at = Column(TIMESTAMP, default=datetime.utcnow)
started_at = Column(TIMESTAMP)
completed_at = Column(TIMESTAMP)
# 同一 OSS 对象被后续成功打包覆盖时标记;默认历史列表不展示。
superseded_at = Column(TIMESTAMP)
# 产物
ipa_path = Column(String)
+7
View File
@@ -103,12 +103,19 @@ def _enrich_tasks(tasks):
@router.get("", response_model=List[TaskResponse])
async def list_tasks(
status: str = None,
include_superseded: bool = False,
limit: int = 20,
offset: int = 0,
user: dict = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取任务列表"""
query = db.query(Task)
if include_superseded:
if not user.get("is_admin"):
raise HTTPException(status_code=403, detail="需要管理员权限")
else:
query = query.filter(Task.superseded_at.is_(None))
if status:
query = query.filter(Task.status == status)
tasks = query.order_by(Task.created_at.desc()).offset(offset).limit(limit).all()
+1
View File
@@ -27,6 +27,7 @@ class TaskResponse(BaseModel):
created_at: Optional[datetime]
started_at: Optional[datetime]
completed_at: Optional[datetime]
superseded_at: Optional[datetime] = None
ipa_path: Optional[str]
oss_url: Optional[str]
dsym_path: Optional[str]
+47 -1
View File
@@ -6,7 +6,7 @@ import plistlib
import shutil
import subprocess
import zipfile
from datetime import datetime
from datetime import datetime, timedelta
from pathlib import Path
from typing import List
@@ -214,6 +214,7 @@ def _cleanup_old_builds(db):
base = BUILD_BASE_DIR
if not base.exists():
_cleanup_superseded_tasks(db)
return
for d in base.iterdir():
if d.is_dir() and d.name.startswith("build_readoor_"):
@@ -222,6 +223,50 @@ def _cleanup_old_builds(db):
shutil.rmtree(d)
except Exception:
pass
_cleanup_superseded_tasks(db)
def _cleanup_superseded_tasks(db):
"""7 天后删除已被新 OSS 包覆盖的历史记录及仅本地保留的文件。"""
from ..models import Task
cutoff = datetime.utcnow() - timedelta(days=7)
tasks = db.query(Task).filter(
Task.superseded_at.isnot(None), Task.superseded_at < cutoff,
).all()
for task in tasks:
for path_value in (task.build_dir, task.dsym_path, task.obfuscation_maps_path):
if not path_value:
continue
try:
path = Path(path_value)
if path.is_dir():
shutil.rmtree(path, ignore_errors=True)
elif path.exists():
path.unlink()
except OSError:
pass
log_path = Path(__file__).parent.parent / "logs" / f"{task.id}.log"
try:
log_path.unlink(missing_ok=True)
except OSError:
pass
db.delete(task)
if tasks:
db.commit()
def _mark_superseded_tasks(db, latest_task):
"""标记同一 OSS 对象的旧成功记录;OSS 文件由最新任务覆盖。"""
if not latest_task.oss_url:
return
from ..models import Task
db.query(Task).filter(
Task.id != latest_task.id,
Task.status == "completed",
Task.oss_url == latest_task.oss_url,
Task.superseded_at.is_(None),
).update({Task.superseded_at: datetime.utcnow()}, synchronize_session=False)
db.commit()
def _cleanup_old_logs():
@@ -439,6 +484,7 @@ async def run_build_task(task_id: str):
await asyncio.to_thread(_db_update, db, task,
status="completed", completed_at=datetime.utcnow(),
current_step=None)
await asyncio.to_thread(_mark_superseded_tasks, db, task)
await log_streamer.emit_step(task_id, "打包完成")
await log_streamer.emit(task_id, f"IPA: {ipa_path}")