feat: 构建超时控制、日志持久化、任务删除、pod install 及多项 UI 优化

- 新增 BUILD_TIMEOUT_HOURS 配置,超时自动标记失败
- 日志保存到独立 backend/logs/ 目录,不随构建目录删除
- 新增任务删除 API 及前端删除按钮
- 构建流程增加 pod install 步骤
- 通过文件传递配置避免命令行参数截断
- Provisioning Profile 支持按名称搜索
- dSYM 下载支持目录打包为 zip
- 前端端口可配置、历史页增加下载列和耗时显示
- 日志弹窗支持展开/收起详细日志
- 自动生成 upload_key 标识
This commit is contained in:
shen
2026-06-08 18:56:43 +08:00
parent 75363ee276
commit f5919a8229
11 changed files with 535 additions and 212 deletions
+34 -8
View File
@@ -64,6 +64,12 @@ class LogStreamer:
if not queue:
return
# 回放已缓存的历史日志
existing = self._log_lines.get(task_id, [])
for entry in existing:
yield entry
# 流式推送新消息
while True:
try:
msg = await asyncio.wait_for(queue.get(), timeout=30)
@@ -89,12 +95,15 @@ class LogStreamer:
self._queues.pop(task_id, None)
def save_log(self, task_id: str, build_dir: Path):
"""将收集的日志保存到打包目录"""
lines = self._log_lines.pop(task_id, None)
"""将收集的日志保存到独立日志目录(不会随构建目录删除)"""
lines = self._log_lines.get(task_id)
if not lines:
return
log_path = build_dir / "build.log"
# 保存到 backend/logs/ 目录
logs_dir = Path(__file__).parent.parent / "logs"
logs_dir.mkdir(exist_ok=True)
log_path = logs_dir / f"{task_id}.log"
with open(log_path, "w", encoding="utf-8") as f:
for entry in lines:
ts = entry["timestamp"]
@@ -102,11 +111,28 @@ class LogStreamer:
msg = entry["message"]
f.write(f"[{ts}] [{level}] {msg}\n")
def get_log(self, build_dir: Path) -> str:
"""读取已保存的打包日志"""
log_path = build_dir / "build.log"
if log_path.exists():
return log_path.read_text(encoding="utf-8")
# 同时保存到构建目录(如果存在)
if build_dir and build_dir.exists():
with open(build_dir / "build.log", "w", encoding="utf-8") as f:
for entry in lines:
ts = entry["timestamp"]
level = entry["level"].upper()
msg = entry["message"]
f.write(f"[{ts}] [{level}] {msg}\n")
def get_log(self, build_dir: Path, task_id: str = None) -> str:
"""读取已保存的打包日志(优先从独立日志目录读取)"""
# 优先从独立日志目录读取
if task_id:
logs_dir = Path(__file__).parent.parent / "logs"
log_path = logs_dir / f"{task_id}.log"
if log_path.exists():
return log_path.read_text(encoding="utf-8")
# 回退到构建目录
if build_dir:
log_path = build_dir / "build.log"
if log_path.exists():
return log_path.read_text(encoding="utf-8")
return ""