deploy_day.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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. # git add
  61. result = subprocess.run(
  62. ["git", "add", "."],
  63. capture_output=True, text=True, cwd=PROJECT_ROOT
  64. )
  65. if result.returncode != 0:
  66. print(f"❌ git add 失败:{result.stderr}")
  67. return False
  68. # git commit
  69. commit_msg = f"Deploy Day {day}: {topic}"
  70. result = subprocess.run(
  71. ["git", "commit", "-m", commit_msg],
  72. capture_output=True, text=True, cwd=PROJECT_ROOT
  73. )
  74. if result.returncode != 0:
  75. print(f"❌ git commit 失败:{result.stderr}")
  76. return False
  77. print(f"✅ Git commit: {commit_msg}")
  78. # git tag
  79. tag_name = f"v_day{day}_{topic.replace(' ', '_')}"
  80. result = subprocess.run(
  81. ["git", "tag", tag_name],
  82. capture_output=True, text=True, cwd=PROJECT_ROOT
  83. )
  84. if result.returncode != 0:
  85. print(f"❌ git tag 失败:{result.stderr}")
  86. return False
  87. print(f"✅ Git tag: {tag_name}")
  88. return True
  89. except Exception as e:
  90. print(f"❌ Git 操作异常:{e}")
  91. return False
  92. def git_push() -> bool:
  93. """Git 推送"""
  94. try:
  95. result = subprocess.run(
  96. ["git", "push", "origin", "main", "--tags"],
  97. capture_output=True, text=True, cwd=PROJECT_ROOT
  98. )
  99. if result.returncode != 0:
  100. print(f"❌ git push 失败:{result.stderr}")
  101. return False
  102. print("✅ Git push 成功")
  103. return True
  104. except Exception as e:
  105. print(f"❌ Git push 异常:{e}")
  106. return False
  107. def send_notification(day: int, topic: str, config: dict) -> bool:
  108. """发送通知(通过 OpenClaw message 工具)"""
  109. if not config.get("notification", {}).get("enabled", False):
  110. print("⚠️ 通知已禁用")
  111. return True
  112. template = config["notification"]["template"]
  113. message = template.format(topic=topic)
  114. print(f"\n📱 通知消息:\n{message}")
  115. # TODO: 调用 OpenClaw message 工具发送
  116. # 这里需要集成 OpenClaw 的 message 工具
  117. # 目前先打印消息,用户手动发送
  118. return True
  119. def main():
  120. parser = argparse.ArgumentParser(description="部署单日课程")
  121. parser.add_argument("--day", type=int, required=True, help="天数")
  122. parser.add_argument("--topic", type=str, required=True, help="主题名称")
  123. args = parser.parse_args()
  124. config = load_config()
  125. print("=" * 50)
  126. print(f"🚀 部署 Day {args.day}: {args.topic}")
  127. print("=" * 50)
  128. # 移动文件
  129. if not move_files(args.day, config):
  130. print("❌ 文件移动失败")
  131. sys.exit(1)
  132. # Git 提交
  133. if not git_commit(args.day, args.topic):
  134. print("❌ Git 提交失败")
  135. sys.exit(1)
  136. # Git 推送
  137. if not git_push():
  138. print("❌ Git 推送失败")
  139. sys.exit(1)
  140. # 发送通知
  141. send_notification(args.day, args.topic, config)
  142. print("\n🎉 部署完成!")
  143. if __name__ == "__main__":
  144. main()