deploy_day.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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__).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. # 移动 HTML
  26. html_src = staging_dir / f"course_day{day}.html"
  27. html_dst = courseware_dir / f"course_day{day}.html"
  28. if html_src.exists():
  29. shutil.copy2(html_src, html_dst)
  30. print(f"✅ 移动课程 HTML: {html_dst}")
  31. else:
  32. print(f"❌ 课程 HTML 不存在:{html_src}")
  33. return False
  34. # 移动练习题
  35. exercise_src = staging_dir / "exercises" / f"day{day}_task.py"
  36. exercise_dst = exercises_dir / f"day{day}_task.py"
  37. if exercise_src.exists():
  38. shutil.copy2(exercise_src, exercise_dst)
  39. print(f"✅ 移动练习题:{exercise_dst}")
  40. else:
  41. print(f"❌ 练习题不存在:{exercise_src}")
  42. return False
  43. # 移动测试
  44. test_src = staging_dir / "tests" / f"test_day{day}.py"
  45. test_dst = tests_dir / f"test_day{day}.py"
  46. if test_src.exists():
  47. shutil.copy2(test_src, test_dst)
  48. print(f"✅ 移动测试用例:{test_dst}")
  49. else:
  50. print(f"❌ 测试用例不存在:{test_src}")
  51. return False
  52. return True
  53. def git_commit(day: int, topic: str) -> bool:
  54. """Git 提交"""
  55. try:
  56. # git add
  57. result = subprocess.run(
  58. ["git", "add", "."],
  59. capture_output=True, text=True, cwd=PROJECT_ROOT
  60. )
  61. if result.returncode != 0:
  62. print(f"❌ git add 失败:{result.stderr}")
  63. return False
  64. # git commit
  65. commit_msg = f"Deploy Day {day}: {topic}"
  66. result = subprocess.run(
  67. ["git", "commit", "-m", commit_msg],
  68. capture_output=True, text=True, cwd=PROJECT_ROOT
  69. )
  70. if result.returncode != 0:
  71. print(f"❌ git commit 失败:{result.stderr}")
  72. return False
  73. print(f"✅ Git commit: {commit_msg}")
  74. # git tag
  75. tag_name = f"v_day{day}_{topic.replace(' ', '_')}"
  76. result = subprocess.run(
  77. ["git", "tag", tag_name],
  78. capture_output=True, text=True, cwd=PROJECT_ROOT
  79. )
  80. if result.returncode != 0:
  81. print(f"❌ git tag 失败:{result.stderr}")
  82. return False
  83. print(f"✅ Git tag: {tag_name}")
  84. return True
  85. except Exception as e:
  86. print(f"❌ Git 操作异常:{e}")
  87. return False
  88. def git_push() -> bool:
  89. """Git 推送"""
  90. try:
  91. result = subprocess.run(
  92. ["git", "push", "origin", "main", "--tags"],
  93. capture_output=True, text=True, cwd=PROJECT_ROOT
  94. )
  95. if result.returncode != 0:
  96. print(f"❌ git push 失败:{result.stderr}")
  97. return False
  98. print("✅ Git push 成功")
  99. return True
  100. except Exception as e:
  101. print(f"❌ Git push 异常:{e}")
  102. return False
  103. def send_notification(day: int, topic: str, config: dict) -> bool:
  104. """发送通知(通过 OpenClaw message 工具)"""
  105. if not config.get("notification", {}).get("enabled", False):
  106. print("⚠️ 通知已禁用")
  107. return True
  108. template = config["notification"]["template"]
  109. message = template.format(topic=topic)
  110. print(f"\n📱 通知消息:\n{message}")
  111. # TODO: 调用 OpenClaw message 工具发送
  112. # 这里需要集成 OpenClaw 的 message 工具
  113. # 目前先打印消息,用户手动发送
  114. return True
  115. def main():
  116. parser = argparse.ArgumentParser(description="部署单日课程")
  117. parser.add_argument("--day", type=int, required=True, help="天数")
  118. parser.add_argument("--topic", type=str, required=True, help="主题名称")
  119. args = parser.parse_args()
  120. config = load_config()
  121. print("=" * 50)
  122. print(f"🚀 部署 Day {args.day}: {args.topic}")
  123. print("=" * 50)
  124. # 移动文件
  125. if not move_files(args.day, config):
  126. print("❌ 文件移动失败")
  127. sys.exit(1)
  128. # Git 提交
  129. if not git_commit(args.day, args.topic):
  130. print("❌ Git 提交失败")
  131. sys.exit(1)
  132. # Git 推送
  133. if not git_push():
  134. print("❌ Git 推送失败")
  135. sys.exit(1)
  136. # 发送通知
  137. send_notification(args.day, args.topic, config)
  138. print("\n🎉 部署完成!")
  139. if __name__ == "__main__":
  140. main()