Files
user_service/build.py
2025-10-09 11:52:28 +08:00

266 lines
11 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 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("开始清除虚悬镜像...")
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)
# 尝试设置目录可写权限(解决目标目录权限问题)
try:
os.chmod(images_dir, 0o755)
except PermissionError:
print(f"警告:无法修改目录权限 {images_dir},可能导致文件复制失败")
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 not os.path.exists(src_path):
print(f"警告:源文件不存在 {src_path},跳过")
continue
if not os.access(src_path, os.R_OK):
print(f"警告:无权限读取 {src_path},尝试修复权限...")
try:
# 尝试赋予源文件读权限
os.chmod(src_path, 0o644)
# 再次检查权限
if not os.access(src_path, os.R_OK):
print(f"错误:仍无法读取 {src_path},请手动检查权限")
continue
except PermissionError:
print(f"错误:无法修复 {src_path} 权限,跳过该文件")
continue
# 处理目标文件
if os.path.exists(dest_path):
try:
os.remove(dest_path)
print(f"已删除旧文件: {dest_path}")
except PermissionError:
print(f"警告:无权限删除旧文件 {dest_path}尝试用sudo运行脚本")
continue
# 复制文件(带权限处理)
try:
shutil.copy2(src_path, dest_path)
# 复制后确保目标文件可读写
os.chmod(dest_path, 0o644)
print(f"已将 {src_path} 替换复制到 {dest_path}")
except PermissionError:
print(f"错误:复制 {src_path} 失败(权限不足)")
print("建议:使用 sudo 权限重新运行脚本sudo python3 build.py")
return False
except Exception as e:
print(f"复制文件时发生错误: {str(e)},跳过该文件")
continue
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',
'services': {},
'networks': {},
'volumes': {}
}
yaml_files = []
# 根目录下的 docker-compose.*.yaml
for file in os.listdir(root_dir):
if file.startswith('docker-compose.') and file.endswith('.yaml') and file != 'docker-compose.yaml':
yaml_files.append(os.path.join(root_dir, file))
# api_ 目录下的 docker-compose.*.yaml排除api_template
for dir_name in os.listdir(root_dir):
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}")
try:
with open(file_path, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
if not data:
continue
if 'services' in data:
merged['services'].update(data['services'])
if 'networks' in data:
merged['networks'].update(data['networks'])
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
except PermissionError:
print(f"错误:无权限读取 {file_path},跳过该文件")
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')
try:
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目录可写
try:
os.chmod(deploy_dir, 0o755)
except PermissionError:
print(f"警告:无法修改目录权限 {deploy_dir}")
dest_compose_path = os.path.join(deploy_dir, 'docker-compose.yaml')
# 替换方式复制
if os.path.exists(dest_compose_path):
try:
os.remove(dest_compose_path)
except PermissionError:
print(f"警告:无权限删除旧文件 {dest_compose_path}")
return False
shutil.copy2(output_path, dest_compose_path)
# 设置目标文件权限
os.chmod(dest_compose_path, 0o644)
print(f"已将 {output_path} 替换复制到 {dest_compose_path}")
except PermissionError as e:
print(f"错误:无权限操作文件 {output_path}{dest_compose_path}")
print(f"详细错误: {str(e)}")
print("建议:使用 sudo 权限重新运行脚本sudo python3 build.py")
return False
except Exception as e:
print(f"生成或复制docker-compose.yaml时出错: {str(e)}")
return False
print("\n===== 清理虚悬镜像 =====")
if not cleanup_dangling_images():
print("清除虚悬镜像失败(非致命错误,继续部署流程)")
else:
print("虚悬镜像清除完成")
if __name__ == "__main__":
# 需要安装 pyyaml 库: pip install pyyaml
main()