Files
user_service/build_service.py
2025-10-07 10:32:46 +08:00

191 lines
7.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
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("开始清除虚悬镜像...")
# 清除所有<none>:<none>的虚悬镜像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脚本...")
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
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 stop_docker_compose(root_dir: str) -> bool:
"""停止docker-compose"""
print("开始停止docker-compose...")
compose_file = os.path.join(root_dir, 'docker-compose.yaml')
if not os.path.exists(compose_file):
print(f"未找到docker-compose文件: {compose_file}")
return False
return run_shell_command(f"sudo docker-compose -f {compose_file} down", root_dir)
def start_docker_compose(root_dir: str) -> bool:
"""启动docker-compose"""
print("开始启动docker-compose...")
compose_file = os.path.join(root_dir, 'docker-compose.yaml')
if not os.path.exists(compose_file):
print(f"未找到docker-compose文件: {compose_file}")
return False
return run_shell_command(f"sudo docker-compose -f {compose_file} up -d", root_dir)
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}")
# 3. 启动docker-compose
stop_docker_compose(root_dir)
if not start_docker_compose(root_dir):
print("启动docker-compose失败")
return
print("部署流程完成")
print("\n===== 清理虚悬镜像 =====")
if not cleanup_dangling_images():
print("清除虚悬镜像失败(非致命错误,继续部署流程)")
else:
print("虚悬镜像清除完成")
if __name__ == "__main__":
# 需要安装 pyyaml 库: pip install pyyaml
main()