|
@@ -0,0 +1,110 @@
|
|
|
|
|
+"""run_pipeline — 九任务串行总控(一次登录, 全程共用会话)。
|
|
|
|
|
+
|
|
|
|
|
+用法:
|
|
|
|
|
+ cd pipeline_bot
|
|
|
|
|
+ uv run python pipeline/run_pipeline.py \
|
|
|
|
|
+ [-u longwu988] [-p Longwu1988] [--woma-minutes 30] \
|
|
|
|
|
+ [--zone 0] [--role-name ""] [--login-port 0] [--dry-run]
|
|
|
|
|
+"""
|
|
|
|
|
+from __future__ import annotations
|
|
|
|
|
+
|
|
|
|
|
+import argparse
|
|
|
|
|
+import importlib
|
|
|
|
|
+import logging
|
|
|
|
|
+import sys
|
|
|
|
|
+import time
|
|
|
|
|
+from pathlib import Path
|
|
|
|
|
+
|
|
|
|
|
+# 脚本方式运行(python pipeline/run_pipeline.py)时 sys.path[0]=pipeline/,
|
|
|
|
|
+# 项目根不入 path 则 `pipeline` 包与任务模块不可导入(uv 只安装 src/pbot)
|
|
|
|
|
+_ROOT = str(Path(__file__).resolve().parents[1])
|
|
|
|
|
+if _ROOT not in sys.path:
|
|
|
|
|
+ sys.path.insert(0, _ROOT)
|
|
|
|
|
+
|
|
|
|
|
+from pipeline.ctx import TaskResult # noqa: E402
|
|
|
|
|
+
|
|
|
|
|
+TASKS: list[str] = [
|
|
|
|
|
+ "pipeline.tasks.t01_heal",
|
|
|
|
|
+ "pipeline.tasks.t02_biqi_kill20",
|
|
|
|
|
+ "pipeline.tasks.t03_cave_kill20",
|
|
|
|
|
+ "pipeline.tasks.t04_mine_kill20",
|
|
|
|
|
+ "pipeline.tasks.t05_wugong_kill40",
|
|
|
|
|
+ "pipeline.tasks.t06_shentong",
|
|
|
|
|
+ "pipeline.tasks.t07_wuxing",
|
|
|
|
|
+ "pipeline.tasks.t08_woma_farm",
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+# 失败后可继续的(软失败): T6 神通(等级信号弱判定) — 其余硬失败即停
|
|
|
|
|
+_SOFT: set[str] = {"pipeline.tasks.t06_shentong"}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def parse_args() -> argparse.Namespace:
|
|
|
|
|
+ """解析 CLI 参数。"""
|
|
|
|
|
+ ap = argparse.ArgumentParser(prog="run_pipeline")
|
|
|
|
|
+ _ = ap.add_argument("-u", "--user", default="longwu988")
|
|
|
|
|
+ _ = ap.add_argument("-p", "--password", default="Longwu1988")
|
|
|
|
|
+ _ = ap.add_argument("--woma-minutes", type=float, default=30.0)
|
|
|
|
|
+ _ = ap.add_argument("--zone", type=int, default=0, help="0=最新区")
|
|
|
|
|
+ _ = ap.add_argument("--role-name", default="", help="空=随机中文名")
|
|
|
|
|
+ _ = ap.add_argument("--login-port", type=int, default=0)
|
|
|
|
|
+ _ = ap.add_argument("--dry-run", action="store_true",
|
|
|
|
|
+ help="只打印任务序列, 不登录")
|
|
|
|
|
+ return ap.parse_args()
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def main() -> int:
|
|
|
|
|
+ """dry-run 打印任务序列; 正式跑登录一次串行执行, 软失败继续硬失败停。"""
|
|
|
|
|
+ a = parse_args()
|
|
|
|
|
+ logging.basicConfig(
|
|
|
|
|
+ level=logging.INFO,
|
|
|
|
|
+ format="%(asctime)s %(levelname)-7s %(name)s: %(message)s",
|
|
|
|
|
+ stream=sys.stdout,
|
|
|
|
|
+ )
|
|
|
|
|
+ log = logging.getLogger("pipeline")
|
|
|
|
|
+
|
|
|
|
|
+ if a.dry_run:
|
|
|
|
|
+ print("[dry-run] 任务序列:") # noqa: T201 — dry-run 冒烟输出为硬需求
|
|
|
|
|
+ for t in TASKS:
|
|
|
|
|
+ mod = importlib.import_module(t)
|
|
|
|
|
+ print(f" - {getattr(mod, 'TASK_TITLE', t)}") # noqa: T201
|
|
|
|
|
+ print(f"[dry-run] 参数: zone={a.zone} role={a.role_name or '<随机>'} " # noqa: T201
|
|
|
|
|
+ f"woma={a.woma_minutes}min")
|
|
|
|
|
+ return 0
|
|
|
|
|
+
|
|
|
|
|
+ from pipeline.bootstrap import bootstrap_session
|
|
|
|
|
+ ctx, real_name = bootstrap_session(
|
|
|
|
|
+ user=a.user, password=a.password, woma_minutes=a.woma_minutes,
|
|
|
|
|
+ login_port=a.login_port, force_zone=a.zone,
|
|
|
|
|
+ role_name=a.role_name)
|
|
|
|
|
+ log.info("会话就绪: 区=%s 角色=%s", ctx.zone_label, real_name)
|
|
|
|
|
+
|
|
|
|
|
+ exit_code = 0
|
|
|
|
|
+ try:
|
|
|
|
|
+ for name in TASKS:
|
|
|
|
|
+ mod = importlib.import_module(name)
|
|
|
|
|
+ title = getattr(mod, "TASK_TITLE", name)
|
|
|
|
|
+ log.info("======== %s ========", title)
|
|
|
|
|
+ t0 = time.time()
|
|
|
|
|
+ try:
|
|
|
|
|
+ result: TaskResult = mod.run_task(ctx)
|
|
|
|
|
+ except Exception as e: # noqa: BLE001 — 单任务异常不拖垮全链
|
|
|
|
|
+ result = TaskResult.fail(f"异常: {e!r}")
|
|
|
|
|
+ dt = time.time() - t0
|
|
|
|
|
+ ctx.checklist.mark(title, result.ok, result.detail)
|
|
|
|
|
+ log.info("[%s] %s (%.0fs)", "✓" if result.ok else "✗",
|
|
|
|
|
+ result.detail, dt)
|
|
|
|
|
+ if not result.ok and name not in _SOFT:
|
|
|
|
|
+ log.error("硬失败 — 流水线终止")
|
|
|
|
|
+ exit_code = 1
|
|
|
|
|
+ break
|
|
|
|
|
+ if not result.ok:
|
|
|
|
|
+ log.warning("软失败 — 继续下一任务")
|
|
|
|
|
+ finally:
|
|
|
|
|
+ ctx.stop[0] = True
|
|
|
|
|
+ ctx.conn.close()
|
|
|
|
|
+ log.info("会话已关闭; 打勾文件: %s", ctx.checklist.path)
|
|
|
|
|
+ return exit_code
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ sys.exit(main())
|