#!/usr/bin/env python import os import sys import subprocess import threading from concurrent.futures import ThreadPoolExecutor, as_completed import yaml import requests import logging TIMEOUT = 300 # 初始化日志记录器 logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", handlers=[logging.StreamHandler(sys.stdout)] ) LOCK = threading.Lock() modified = False def download_file(url): """使用 requests 下载文件内容,限制超时时间为 3 分钟""" try: response = requests.get(url, timeout=TIMEOUT) response.raise_for_status() return response.text except requests.exceptions.RequestException as e: logging.error(f"❌ 下载 {url} 时出错: {e}") return None def process_item(item): global modified file_path = item.get("file") source = item.get("source") if not file_path or not source: logging.warning(f"⚠️ 条目缺少 file 或 source 字段: {item}") return logging.info(f"🛠️ 处理 {file_path} 从 {source}") new_content = download_file(source) if new_content is None: return # 构建最终文件路径 abs_file_path = os.path.join(os.getcwd(), file_path) os.makedirs(os.path.dirname(abs_file_path), exist_ok=True) updated = False if os.path.exists(abs_file_path): with open(abs_file_path, "r", encoding="utf-8") as f: old_content = f.read() if old_content != new_content: updated = True else: logging.warning(f"⚠️ 文件 {abs_file_path} 不存在,将创建。") updated = True if updated: with open(abs_file_path, "w", encoding="utf-8") as f: f.write(new_content) logging.info(f"🚀 更新了 {file_path}") with LOCK: modified = True def check_env_vars(): """检查必要的环境变量是否存在""" required_vars = ["THE_REPO", "REPO_TOKEN", "CI_EMAIL"] missing_vars = [var for var in required_vars if not os.environ.get(var)] if missing_vars: logging.error(f"❌ 缺少必要的环境变量: {', '.join(missing_vars)}") sys.exit(1) def main(): global modified # 检查环境变量 check_env_vars() # 加载 list.yaml 文件 try: list_yaml_path = os.path.join(os.getcwd(), "list.yaml") with open(list_yaml_path, "r", encoding="utf-8") as f: items = yaml.safe_load(f) or [] except FileNotFoundError: logging.error("❌ list.yaml 文件不存在。") sys.exit(0) except yaml.YAMLError as e: logging.error(f"❌ 解析 list.yaml 文件时出错: {e}") sys.exit(1) logging.info("🚀 开始并发更新文件") # 使用线程池执行多线程任务 max_workers = int(os.getenv("MAX_WORKERS", 2)) # 允许通过环境变量配置并发数 with ThreadPoolExecutor(max_workers=max_workers) as executor: futures = [executor.submit(process_item, item) for item in items] for future in as_completed(futures): try: future.result() # 确保异常被捕获 except Exception as e: logging.error(f"❌ 处理任务时出错: {e}") if modified: logging.info("📦 更新完成,正在提交更改...") try: # 配置 Git 用户信息 ci_email = os.environ["CI_EMAIL"] subprocess.run(["git", "config", "--global", "user.email", ci_email], check=True) subprocess.run(["git", "config", "--global", "user.name", "CI/CD"], check=True) # 设置远程仓库 URL(隐藏敏感信息) repo_url = os.environ["THE_REPO"] token = os.environ["REPO_TOKEN"] new_repo_url = repo_url.replace("https://", f"https://oauth2:{token}@") subprocess.run(["git", "remote", "set-url", "origin", new_repo_url], check=True) # 提交并推送更改 subprocess.run(["git", "checkout", "-B", "main"], check=True) subprocess.run(["git", "add", "."], check=True) commit_message = "feat: 自动更新从 list.yaml 下载最新文件" subprocess.run(["git", "commit", "-m", commit_message], check=False) subprocess.run(["git", "push", "origin", "main"], check=True) except subprocess.CalledProcessError as e: logging.error(f"❌ Git 操作失败: {e}") sys.exit(1) else: logging.info("📦 没有需要更新的文件。") if __name__ == "__main__": main()