deploy_day.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #!/usr/bin/env python3
  2. """部署单日课程(每天 14:00)"""
  3. import argparse
  4. import os
  5. import shutil
  6. import subprocess
  7. import sys
  8. from pathlib import Path
  9. import yaml
  10. PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent.parent
  11. def load_config() -> dict:
  12. """加载配置"""
  13. config_path = PROJECT_ROOT / "skills" / "mathlab" / "config.yaml"
  14. with open(config_path, "r", encoding="utf-8") as f:
  15. return yaml.safe_load(f)
  16. def move_files(day: int, config: dict) -> bool:
  17. """从 staging 移动到正式目录"""
  18. staging_dir = PROJECT_ROOT / config["output"]["staging"]
  19. courseware_dir = PROJECT_ROOT / config["output"]["courseware"]
  20. exercises_dir = PROJECT_ROOT / config["output"]["exercises"]
  21. tests_dir = PROJECT_ROOT / config["output"]["tests"]
  22. # 确保目标目录存在
  23. for dir_path in [courseware_dir, exercises_dir, tests_dir]:
  24. dir_path.mkdir(parents=True, exist_ok=True)
  25. # 检查是否已部署
  26. html_dst = courseware_dir / f"course_day{day}.html"
  27. exercise_dst = exercises_dir / f"day{day}_task.py"
  28. test_dst = tests_dir / f"test_day{day}.py"
  29. if html_dst.exists() and exercise_dst.exists() and test_dst.exists():
  30. print(f"⚠️ Day {day} 已部署,跳过")
  31. return True # 已部署,返回成功
  32. # 移动 HTML
  33. html_src = staging_dir / f"course_day{day}.html"
  34. if html_src.exists():
  35. shutil.copy2(html_src, html_dst)
  36. print(f"✅ 移动课程 HTML: {html_dst}")
  37. else:
  38. print(f"❌ 课程 HTML 不存在:{html_src}")
  39. return False
  40. # 移动练习题
  41. exercise_src = staging_dir / "exercises" / f"day{day}_task.py"
  42. if exercise_src.exists():
  43. shutil.copy2(exercise_src, exercise_dst)
  44. print(f"✅ 移动练习题:{exercise_dst}")
  45. else:
  46. print(f"❌ 练习题不存在:{exercise_src}")
  47. return False
  48. # 移动测试
  49. test_src = staging_dir / "tests" / f"test_day{day}.py"
  50. if test_src.exists():
  51. shutil.copy2(test_src, test_dst)
  52. print(f"✅ 移动测试用例:{test_dst}")
  53. else:
  54. print(f"❌ 测试用例不存在:{test_src}")
  55. return False
  56. return True
  57. def git_commit(day: int, topic: str) -> bool:
  58. """Git 提交"""
  59. try:
  60. # 检查是否已有 tag
  61. tag_name = f"v_day{day}_{topic.replace(' ', '_')}"
  62. result = subprocess.run(
  63. ["git", "tag", "-l", tag_name],
  64. capture_output=True, text=True, cwd=PROJECT_ROOT
  65. )
  66. if result.returncode == 0 and result.stdout.strip():
  67. print(f"⚠️ Tag {tag_name} 已存在,跳过提交")
  68. return True # 已部署,返回成功
  69. # git add
  70. result = subprocess.run(
  71. ["git", "add", "."],
  72. capture_output=True, text=True, cwd=PROJECT_ROOT
  73. )
  74. if result.returncode != 0:
  75. print(f"❌ git add 失败:{result.stderr}")
  76. return False
  77. # git commit
  78. commit_msg = f"Deploy Day {day}: {topic}"
  79. result = subprocess.run(
  80. ["git", "commit", "-m", commit_msg],
  81. capture_output=True, text=True, cwd=PROJECT_ROOT
  82. )
  83. if result.returncode != 0:
  84. print(f"❌ git commit 失败:{result.stderr}")
  85. return False
  86. print(f"✅ Git commit: {commit_msg}")
  87. # git tag
  88. result = subprocess.run(
  89. ["git", "tag", tag_name],
  90. capture_output=True, text=True, cwd=PROJECT_ROOT
  91. )
  92. if result.returncode != 0:
  93. print(f"❌ git tag 失败:{result.stderr}")
  94. return False
  95. print(f"✅ Git tag: {tag_name}")
  96. return True
  97. except Exception as e:
  98. print(f"❌ Git 操作异常:{e}")
  99. return False
  100. def git_push(config: dict) -> bool:
  101. """Git 推送"""
  102. try:
  103. branch = config.get("git_branch", "master")
  104. result = subprocess.run(
  105. ["git", "push", "origin", branch, "--tags"],
  106. capture_output=True, text=True, cwd=PROJECT_ROOT
  107. )
  108. if result.returncode != 0:
  109. print(f"❌ git push 失败:{result.stderr}")
  110. return False
  111. print("✅ Git push 成功")
  112. return True
  113. except Exception as e:
  114. print(f"❌ Git push 异常:{e}")
  115. return False
  116. def send_notification(day: int, topic: str, config: dict) -> bool:
  117. """发送通知(通过 OpenClaw message 工具)"""
  118. if not config.get("notification", {}).get("enabled", False):
  119. print("⚠️ 通知已禁用")
  120. return True
  121. template = config["notification"]["template"]
  122. message = template.format(topic=topic)
  123. print(f"\n📱 通知消息:\n{message}")
  124. # TODO: 调用 OpenClaw message 工具发送
  125. # 这里需要集成 OpenClaw 的 message 工具
  126. # 目前先打印消息,用户手动发送
  127. return True
  128. def main():
  129. parser = argparse.ArgumentParser(description="部署单日课程")
  130. parser.add_argument("--day", type=int, required=True, help="天数")
  131. parser.add_argument("--topic", type=str, required=True, help="主题名称")
  132. args = parser.parse_args()
  133. config = load_config()
  134. print("=" * 50)
  135. print(f"🚀 部署 Day {args.day}: {args.topic}")
  136. print("=" * 50)
  137. # 移动文件
  138. if not move_files(args.day, config):
  139. print("❌ 文件移动失败")
  140. sys.exit(1)
  141. # Git 提交
  142. if not git_commit(args.day, args.topic):
  143. print("❌ Git 提交失败")
  144. sys.exit(1)
  145. # Git 推送
  146. if not git_push(config):
  147. print("❌ Git 推送失败")
  148. sys.exit(1)
  149. # 发送通知
  150. send_notification(args.day, args.topic, config)
  151. print("\n🎉 部署完成!")
  152. if __name__ == "__main__":
  153. main()