From ce48587688950d6daf0db78dd18239dd875d69a4 Mon Sep 17 00:00:00 2001 From: vipg Date: Thu, 9 Oct 2025 11:43:56 +0800 Subject: [PATCH] update --- build.py | 192 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 build.py diff --git a/build.py b/build.py new file mode 100644 index 0000000..6da24ae --- /dev/null +++ b/build.py @@ -0,0 +1,192 @@ +import os +import shutil # 新增导入shutil用于文件复制 +import yaml +import subprocess +from typing import Dict, Any + +def run_shell_command(command: str, cwd: str = None) -> bool: + """执行shell命令并返回执行结果""" + try: + print(f"执行命令: {command} (工作目录: {cwd or os.getcwd()})") + result = subprocess.run( + command, + cwd=cwd, + shell=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + print(f"命令输出: {result.stdout}") + return True + except subprocess.CalledProcessError as e: + print(f"命令执行失败: {e.stderr}") + return False + +def cleanup_dangling_images() -> bool: + """清除docker虚悬镜像(dangling images)""" + print("开始清除虚悬镜像...") + # 清除所有:的虚悬镜像,xargs -r确保无镜像时不执行删除命令 + return run_shell_command('sudo docker images -f "dangling=true" -q | xargs -r sudo docker rmi') + +def execute_release_scripts(root_dir: str) -> bool: + """遍历所有api_目录并执行release.sh脚本(排除api_template)""" + print("开始执行所有api_目录下的release.sh脚本...") + + # 定义目标镜像目录 + images_dir = os.path.join(root_dir, 'deploy', 'images') + # 确保目标目录存在 + os.makedirs(images_dir, exist_ok=True) + + for dir_name in os.listdir(root_dir): + # 排除api_template文件夹 + if dir_name == "api_template": + print(f"跳过模板目录: {dir_name}") + continue + + dir_path = os.path.join(root_dir, dir_name) + if os.path.isdir(dir_path) and dir_name.startswith('api_'): + release_script = os.path.join(dir_path, 'release.sh') + if os.path.exists(release_script): + print(f"处理目录: {dir_path}") + + # 添加执行权限 + if not run_shell_command(f"chmod +x {release_script}", dir_path): + print(f"为 {release_script} 添加权限失败,跳过执行") + continue + + # 执行release.sh + if not run_shell_command(f"./release.sh", dir_path): + print(f"{release_script} 执行失败,跳过后续步骤") + return False + + # 复制目录下的.tar文件到根目录的deploy/images下(替换方式) + for file in os.listdir(dir_path): + if file.endswith('.tar'): + src_path = os.path.join(dir_path, file) + dest_path = os.path.join(images_dir, file) + # 如果目标文件存在则先删除 + if os.path.exists(dest_path): + os.remove(dest_path) + # 复制文件 + shutil.copy2(src_path, dest_path) + print(f"已将 {src_path} 替换复制到 {dest_path}") + else: + print(f"{dir_path} 中未找到release.sh,跳过") + + print("所有release.sh脚本执行完成") + return True + +def merge_yaml_files(root_dir: str) -> Dict[str, Any]: + """合并所有 docker-compose 相关 YAML 文件内容(排除api_template)""" + merged = { + 'version': '3.8', # 默认使用 3.8 版本 + 'services': {}, + 'networks': {}, + 'volumes': {} + } + + # 查找所有 docker-compose 相关 YAML 文件 + yaml_files = [] + # 1. 根目录下的 docker-compose.*.yaml + for file in os.listdir(root_dir): + if file.startswith('docker-compose.') and file.endswith('.yaml'): + if file == 'docker-compose.yaml': # 跳过目标文件本身 + continue + yaml_files.append(os.path.join(root_dir, file)) + + # 2. api_ 目录下的 docker-compose.*.yaml(排除api_template) + for dir_name in os.listdir(root_dir): + # 排除api_template文件夹 + if dir_name == "api_template": + print(f"跳过模板目录的yaml文件: {dir_name}") + continue + + dir_path = os.path.join(root_dir, dir_name) + if os.path.isdir(dir_path) and dir_name.startswith('api_'): + for file in os.listdir(dir_path): + if file.startswith('docker-compose.') and file.endswith('.yaml'): + yaml_files.append(os.path.join(dir_path, file)) + + # 合并所有 YAML 文件内容 + for file_path in yaml_files: + print(f"合并文件: {file_path}") + with open(file_path, 'r', encoding='utf-8') as f: + try: + data = yaml.safe_load(f) + if not data: + continue + + # 合并 services + if 'services' in data: + merged['services'].update(data['services']) + + # 合并 networks + if 'networks' in data: + merged['networks'].update(data['networks']) + + # 合并 volumes + if 'volumes' in data: + merged['volumes'].update(data['volumes']) + + # 保留最高版本号 + if 'version' in data and data['version'] > merged['version']: + merged['version'] = data['version'] + + except yaml.YAMLError as e: + print(f"解析 {file_path} 失败: {e}") + continue + + return merged + +def main(): + # 先执行清除虚悬镜像操作 + print("部署流程开始,先执行清除虚悬镜像操作...") + if not cleanup_dangling_images(): + print("清除虚悬镜像失败(非致命错误,继续部署流程)") + else: + print("虚悬镜像清除完成") + + # 获取项目根目录(deploy.py 所在目录) + root_dir = os.path.dirname(os.path.abspath(__file__)) + print(f"项目根目录: {root_dir}") + + # 1. 执行所有api_目录下的release.sh脚本 + if not execute_release_scripts(root_dir): + print("执行release脚本失败,终止部署流程") + return + + # 2. 合并所有YAML文件 + merged_data = merge_yaml_files(root_dir) + + # 输出到根目录的docker-compose.yaml + output_path = os.path.join(root_dir, 'docker-compose.yaml') + with open(output_path, 'w', encoding='utf-8') as f: + yaml.dump( + merged_data, + f, + sort_keys=False, # 保持键的顺序 + allow_unicode=True, # 支持中文 + default_flow_style=False # 使用块样式 + ) + print(f"已生成合并后的 docker-compose.yaml: {output_path}") + + # 将新的docker-compose.yaml以替换方式复制到根目录的deploy/下 + deploy_dir = os.path.join(root_dir, 'deploy') + os.makedirs(deploy_dir, exist_ok=True) # 确保deploy目录存在 + dest_compose_path = os.path.join(deploy_dir, 'docker-compose.yaml') + # 替换方式复制:若目标存在则先删除 + if os.path.exists(dest_compose_path): + os.remove(dest_compose_path) + shutil.copy2(output_path, dest_compose_path) + print(f"已将 {output_path} 替换复制到 {dest_compose_path}") + + print("\n===== 清理虚悬镜像 =====") + if not cleanup_dangling_images(): + print("清除虚悬镜像失败(非致命错误,继续部署流程)") + else: + print("虚悬镜像清除完成") + +if __name__ == "__main__": + # 需要安装 pyyaml 库: pip install pyyaml + main() \ No newline at end of file