440 lines
15 KiB
Python
440 lines
15 KiB
Python
import datetime
|
||
import json
|
||
import os
|
||
import shutil
|
||
import sys
|
||
import time
|
||
|
||
curr_dir = os.path.split(os.path.abspath(__file__))[0]
|
||
print("curr_dir = " + curr_dir)
|
||
|
||
# 添加工具路径到系统路径
|
||
tools_path = os.path.abspath(os.path.join(curr_dir, "../../FindObjectBundleBuilder/Tools"))
|
||
print("tools_path = " + tools_path)
|
||
|
||
# 检查路径是否存在
|
||
# if os.path.exists(tools_path):
|
||
# print("Tools path exists")
|
||
# if os.path.exists(os.path.join(tools_path, "utils.py")):
|
||
# print("utils.py found")
|
||
# else:
|
||
# print("utils.py not found")
|
||
# else:
|
||
# print("Tools path does not exist")
|
||
|
||
sys.path.insert(0, tools_path)
|
||
sys.path.insert(0, os.path.join(tools_path, "ipm"))
|
||
sys.path.insert(0, os.path.join(tools_path, "google_drive"))
|
||
sys.path.insert(0, os.path.join(tools_path, "config_convert"))
|
||
sys.path.insert(0, os.path.join(tools_path, "build_package"))
|
||
|
||
platform = "Android"
|
||
env = "Release"
|
||
if len(sys.argv) > 1:
|
||
platform = sys.argv[1]
|
||
if len(sys.argv) > 1:
|
||
env = sys.argv[2]
|
||
|
||
from google_drive.google_sheet import GoogleSheetHelper
|
||
from firebase.firebase_helper import FirebaseHelper
|
||
from notification_helper import NotificationHelper
|
||
import upload_firebase_storage
|
||
import config as config
|
||
|
||
notification_helper = NotificationHelper()
|
||
# 尝试导入 utils 模块
|
||
try:
|
||
import utils
|
||
|
||
print("utils module imported successfully")
|
||
except ImportError as e:
|
||
print(f"Failed to import utils: {e}")
|
||
|
||
|
||
# 如果导入失败,直接定义 mkdirs 函数
|
||
def mkdirs(dir_path: str):
|
||
if not os.path.exists(dir_path):
|
||
os.makedirs(dir_path)
|
||
|
||
|
||
# 创建一个简单的 utils 对象来模拟模块
|
||
class Utils:
|
||
@staticmethod
|
||
def mkdirs(dir_path: str):
|
||
if not os.path.exists(dir_path):
|
||
os.makedirs(dir_path)
|
||
|
||
|
||
utils = Utils()
|
||
PROJECT_PATH = os.path.abspath(os.path.join(curr_dir, "../build_profile_package"))
|
||
ASSET_PATH = os.path.abspath(os.path.join(curr_dir, "../profile_asset"))
|
||
# Unity执行文件路径
|
||
UNITY_PATH = "/Applications/Unity/Hub/Editor/2021.3.45f1/Unity.app/Contents/MacOS/Unity"
|
||
RESOURCE_ROOT = os.path.abspath(os.path.join(curr_dir, "../build_profile_package/Bundles"))
|
||
REMOTE_CONFIG_FILE = os.path.join(curr_dir, "../../FindObjectBundleBuilder/Tools/firebase/remote_config.json")
|
||
json_path = os.path.abspath(os.path.join(curr_dir, 'temp_config/profile.json'))
|
||
|
||
google_sheet_file_name = 'FIndObject装饰配置表'
|
||
sheet_table_name = 'default'
|
||
|
||
|
||
def get_json():
|
||
''' 访问配置的谷歌表格生成json文件 '''
|
||
|
||
sheet_helper = GoogleSheetHelper()
|
||
sheet = sheet_helper.get_sheet_table(google_sheet_file_name, sheet_table_name)
|
||
sheet_all_row_datas = sheet.get_all_values(major_dimension='ROWS')
|
||
config_json = {}
|
||
error_lines = []
|
||
list_field = ''
|
||
|
||
if sheet is None:
|
||
notification_helper.wechat_alert_exception(
|
||
f'获取表格失败[文件名: {google_sheet_file_name}, 表名: {sheet_table_name}]')
|
||
sheet_all_row_datas = sheet.get_all_values(major_dimension='ROWS')
|
||
field_dict = {}
|
||
field_type_dict = {}
|
||
for i, row_values in enumerate(sheet_all_row_datas):
|
||
if i == 0:
|
||
continue
|
||
# parse_cdn_config(row_values)
|
||
elif i == 1:
|
||
continue
|
||
# parse_remote_config(row_values)
|
||
elif i == 2:
|
||
continue
|
||
elif i == 3:
|
||
parse_field(row_values, field_dict)
|
||
elif i == 4:
|
||
parse_field_type(row_values, field_type_dict)
|
||
else:
|
||
break
|
||
|
||
for i, row_values in enumerate(sheet_all_row_datas):
|
||
if i <= 4:
|
||
continue
|
||
if row_values[0] != '':
|
||
list_field = row_values[0]
|
||
config_json[list_field] = []
|
||
|
||
# if is_row_env_valid(row_values) is False:
|
||
# continue
|
||
|
||
item_data, error = parse_row_data(i + 1, row_values, field_dict, field_type_dict)
|
||
if error != '':
|
||
error_lines.append(error)
|
||
elif len(item_data) == 0:
|
||
continue
|
||
else:
|
||
config_json[list_field].append(item_data)
|
||
|
||
if len(error_lines) > 0:
|
||
notification_helper.append_msg(str(error_lines))
|
||
return
|
||
|
||
_json = json.dumps(config_json)
|
||
local_file_path = get_local_config_file_path()
|
||
utils.write_json_file(local_file_path, "profile.json", _json)
|
||
print(f"json配置生成成功:{len(config_json['datas'])}")
|
||
notification_helper.append_msg(f"json配置生成成功:{len(config_json['datas'])}")
|
||
return _json
|
||
|
||
|
||
def parse_field(row_values, field_dict):
|
||
for i, field_name in enumerate(row_values):
|
||
if i == 0:
|
||
continue
|
||
if field_name != '':
|
||
field_dict[i] = field_name
|
||
print(str(field_dict))
|
||
|
||
|
||
def parse_field_type(row_values, field_type_dict):
|
||
for i, field_type in enumerate(row_values):
|
||
if i == 0:
|
||
continue
|
||
if field_type != '':
|
||
field_type_dict[i] = field_type
|
||
print(str(field_type_dict))
|
||
|
||
|
||
def get_local_config_file_path():
|
||
local_file_path = f'temp_config/'
|
||
return local_file_path
|
||
|
||
|
||
def is_row_env_valid(row_values):
|
||
if row_values[1] == 'TRUE':
|
||
return True
|
||
return False
|
||
|
||
|
||
def parse_row_data(row, row_values, field_dict, field_type_dict):
|
||
item_data = {}
|
||
error = ''
|
||
for i, value in enumerate(row_values):
|
||
if i not in field_dict or i not in field_type_dict:
|
||
continue
|
||
|
||
field = field_dict[i]
|
||
field_type = field_type_dict[i]
|
||
isvalid, check_error = check_field_type_valid(field_type, value)
|
||
if isvalid:
|
||
item_data[field] = get_type_value(field_type, value)
|
||
else:
|
||
error += f'|{check_error}'
|
||
if error != '':
|
||
error = f"第{row}行->id:{item_data['id']}关卡配置错误: {error}"
|
||
return item_data, error
|
||
|
||
|
||
def check_field_type_valid(field_type, value):
|
||
if value is None:
|
||
return False, f'值为空'
|
||
|
||
if value == '': # 默认值
|
||
return True, ''
|
||
|
||
try:
|
||
if field_type == 'int':
|
||
int(value)
|
||
elif field_type == 'float' or field_type == 'double':
|
||
float(value)
|
||
elif field_type == 'bool':
|
||
if value.lower() not in ['true', 'false']:
|
||
return False, f'布尔值格式错误: {value}'
|
||
elif field_type == 'string':
|
||
# 字符串类型不需要特殊验证
|
||
pass
|
||
elif field_type.startswith('List<'):
|
||
inner_type = field_type[5:-1] # 提取 List<type> 中的 type
|
||
if value == '':
|
||
return True, '' # 空列表是有效的
|
||
|
||
items = value.split('#')
|
||
for item in items:
|
||
if item == '':
|
||
continue
|
||
valid, error = check_field_type_valid(inner_type, item)
|
||
if not valid:
|
||
return False, f'列表元素 {item} {error}'
|
||
else:
|
||
return False, f'未知类型: {field_type}'
|
||
|
||
return True, ''
|
||
except ValueError:
|
||
return False, f'类型转换错误: 无法将 {value} 转换为 {field_type}'
|
||
except Exception as e:
|
||
return False, f'验证错误: {str(e)}'
|
||
|
||
|
||
def get_type_value(field_type, value):
|
||
if value is None:
|
||
return None
|
||
|
||
if field_type == 'int':
|
||
if value == '':
|
||
return 0
|
||
return int(value)
|
||
elif field_type == 'float' or field_type == 'double':
|
||
if value == '':
|
||
return 0.0
|
||
return float(value)
|
||
elif field_type == 'bool':
|
||
if value == '':
|
||
return False
|
||
return value.lower() == 'true'
|
||
elif field_type == 'string':
|
||
return value
|
||
elif field_type.startswith('List<'):
|
||
if value == '':
|
||
return []
|
||
inner_type = field_type[5:-1]
|
||
items = value.split('#')
|
||
result = []
|
||
for item in items:
|
||
if item == '':
|
||
continue
|
||
result.append(get_type_value(inner_type, item))
|
||
return result
|
||
else:
|
||
# 未知类型,返回原始值
|
||
return value
|
||
|
||
|
||
def copy_file_to_unity():
|
||
''' 软链接资源到unity里面 '''
|
||
uiraw_path = os.path.join(PROJECT_PATH, "Assets/AssetRaw/UIRaw/")
|
||
|
||
# 确保目录存在
|
||
utils.mkdirs(uiraw_path)
|
||
|
||
# 删除现有的符号链接和文件
|
||
try:
|
||
if os.path.exists(uiraw_path):
|
||
# 删除目录中的所有内容
|
||
for item in os.listdir(uiraw_path):
|
||
# 如果是name_font文件夹,跳过不删除
|
||
if item == "name_font":
|
||
continue
|
||
|
||
item_path = os.path.join(uiraw_path, item)
|
||
if os.path.islink(item_path):
|
||
# 如果是符号链接,直接删除
|
||
os.unlink(item_path)
|
||
elif os.path.isfile(item_path):
|
||
# 如果是普通文件,删除
|
||
os.remove(item_path)
|
||
elif os.path.isdir(item_path):
|
||
# 如果是目录,递归删除
|
||
shutil.rmtree(item_path)
|
||
except Exception as e:
|
||
print(f"清理现有文件时出错: {e}")
|
||
|
||
# 创建新的符号链接
|
||
utils.mkdirs(os.path.dirname(ASSET_PATH))
|
||
for root, dirs, files in os.walk(ASSET_PATH):
|
||
for dir in dirs:
|
||
source_path = os.path.join(root, dir)
|
||
target_path = os.path.join(PROJECT_PATH, f"Assets/AssetRaw/UIRaw/{dir}")
|
||
|
||
# 确保目标路径不存在
|
||
if os.path.exists(target_path) or os.path.islink(target_path):
|
||
if os.path.islink(target_path):
|
||
os.unlink(target_path)
|
||
elif os.path.isdir(target_path):
|
||
shutil.rmtree(target_path)
|
||
else:
|
||
os.remove(target_path)
|
||
|
||
os.symlink(source_path, target_path)
|
||
|
||
# 处理JSON文件
|
||
json_target_path = os.path.join(PROJECT_PATH, f"Assets/AssetRaw/UIRaw/profile.json")
|
||
if os.path.exists(json_target_path) or os.path.islink(json_target_path):
|
||
if os.path.islink(json_target_path):
|
||
os.unlink(json_target_path)
|
||
else:
|
||
os.remove(json_target_path)
|
||
|
||
if os.path.exists(json_path):
|
||
os.symlink(json_path, json_target_path)
|
||
else:
|
||
print("json文件不存在,有问题")
|
||
raise Exception("json文件不存在,有问题")
|
||
|
||
print("软链接拷贝成功")
|
||
|
||
|
||
def build_package():
|
||
''' 运行unity打资源包 '''
|
||
time_build_package_start = time.time()
|
||
params = "AppVersion=" + "1.0.0"
|
||
now = datetime.datetime.now()
|
||
build_number = "{:2d}{:02d}{:02d}{:02d}{:02d}".format(int(now.year - 2000), int(now.month), int(now.day),
|
||
int(now.hour), int(now.minute))
|
||
params = params + " BuildVersion=" + build_number
|
||
# params = params + " ResVersion=" + opts.resversion
|
||
params = params + " Platform=" + platform
|
||
# params = params + " BuildType=" + opts.buildtype
|
||
# params = params + " Mode=" + opts.mode
|
||
params = params + " ResourceRoot=" + RESOURCE_ROOT
|
||
print(params)
|
||
cmd = UNITY_PATH + " -quit " + " -batchmode " + " -projectPath " + PROJECT_PATH + " -executeMethod BuildBundlesHelper.BuildBundles " + params + " -logFile " + PROJECT_PATH + "/Logs/build_log.txt"
|
||
print(cmd)
|
||
# 调用Unity打包
|
||
os.system(cmd)
|
||
|
||
error = ''
|
||
is_build_success = False
|
||
# 热更Bundles文件输出文件夹(Unity输出的源文件)
|
||
output_dir = f"{RESOURCE_ROOT}/{platform}"
|
||
bundle_file_path = output_dir + "/ProfilesFeatureV2/" + build_number
|
||
if (platform == "Android" or platform == "iOS") and os.path.exists(output_dir):
|
||
isAppendNewLine = False
|
||
if not os.path.exists(bundle_file_path):
|
||
error = error + f"资源打包失败 "
|
||
isAppendNewLine = True
|
||
|
||
if isAppendNewLine:
|
||
error = error + "\n"
|
||
|
||
if error == '':
|
||
is_build_success = True
|
||
|
||
time_build_package_end = time.time()
|
||
if is_build_success:
|
||
notification_helper.append_msg(
|
||
f"{platform}平台 YooAsset资源构建成功, cost time:{(time_build_package_end - time_build_package_start):.0f}s")
|
||
else:
|
||
notification_helper.append_msg(f"{platform}平台 YooAsset资源构建失败, error:{error}, 请前往Jenkins查看具体日志")
|
||
# notification_helper.append_at_people(notification_helper.at_zhouzhuo)
|
||
return is_build_success, bundle_file_path
|
||
|
||
|
||
def upload_package(is_build_success, ab_dir):
|
||
''' 上传资源包到storage '''
|
||
print(f"ab_path {ab_dir}")
|
||
if is_build_success:
|
||
print(f"开始上传资源包到firebase")
|
||
# storage_path = "Bundles/{}".format(platform) + f'/Profile/profile-{env}.bundle'
|
||
# # 从ab_dir文件夹下获取所有.bundle文件
|
||
# bundle_file = ""
|
||
# if os.path.exists(ab_dir):
|
||
# for root, dirs, files in os.walk(ab_dir):
|
||
# for file in files:
|
||
# if file.endswith('.bundle'):
|
||
# bundle_file_path = os.path.join(root, file)
|
||
# bundle_file = bundle_file_path
|
||
# print(f"找到bundle文件: {bundle_file_path}")
|
||
# break
|
||
#
|
||
# if bundle_file == "":
|
||
# print(f"没有找到资源包")
|
||
# raise Exception("没有找到资源包")
|
||
|
||
storage_path = "Bundles/{}".format(platform) + f'/ProfilesFeatureV2/{env}'
|
||
tmUploadPackage1 = time.time()
|
||
# generation = helper.upload_file(storage_path, bundle_file)
|
||
upload_firebase_storage.upload_directory(helper, storage_path, ab_dir, ".version")
|
||
tmUploadPackage2 = time.time()
|
||
print(f"firebase上传耗时:{tmUploadPackage2 - tmUploadPackage1}")
|
||
|
||
|
||
def refresh_remote_config(time_stamp):
|
||
''' 刷新云控 '''
|
||
remote_key = "profile_asset_config"
|
||
# with open(REMOTE_CONFIG_FILE, "r") as f:
|
||
# online_txt = f.read()
|
||
#
|
||
# if online_txt != None and online_txt != "":
|
||
# online_json = json.loads(online_txt)
|
||
#
|
||
# value = online_json["parameters"][remote_key]["defaultValue"]["value"]
|
||
# value_json = json.loads(value)
|
||
# value_json[env] = url
|
||
|
||
helper.update_remote_config(None, None, remote_key, env, time_stamp)
|
||
|
||
def get_time_stamp():
|
||
"""返回当前时间的时间戳"""
|
||
return int(time.time())
|
||
|
||
|
||
if __name__ == "__main__":
|
||
# platform = "iOS"
|
||
# env = 'Debug'
|
||
print(1)
|
||
helper = FirebaseHelper()
|
||
get_json()
|
||
copy_file_to_unity()
|
||
tmBuildPackage1 = time.time()
|
||
is_build_success, ab_dir = build_package()
|
||
tmBuildPackage2 = time.time()
|
||
print(f" unity打包耗时:{tmBuildPackage2 - tmBuildPackage1}")
|
||
time.sleep(1)
|
||
upload_package(is_build_success, ab_dir)
|
||
time.sleep(1)
|
||
refresh_remote_config(get_time_stamp())
|
||
print(2)
|