相关文章推荐
玩命的猴子  ·  连接到 PostgreSQL ...·  1 年前    · 
很酷的生姜  ·  js调用c++-掘金·  1 年前    · 
气宇轩昂的香瓜  ·  FinNLP - ...·  1 年前    · 

基于Python的构建脚本(续)

创建C#命令行打包所需要的配置文件

基于Python和Jenkins的Unity自动化打包方案总结(2) 中,我们给C#端的命令行打包接口设计了使用json配置文件接收打包参数的方案。该方案的好处是不需要繁琐的解析很多Unity命令行参数,且添加打包参数更加方便且不易出错。而该方案需要我们生成一个json配置文件,也就是这儿python脚本接下来要做的事情。先上代码:

def main()
    # 接上面
    ############# create build config json	############
    json_path = f'{output_folder}/buildConfig.json'
    print(f'Creating config json to {json_path}')
    build_config = {}
    if BUILD_TYPE == 'PC':
            build_config['OS'] = 0
    elif BUILD_TYPE == 'Android':
            build_config['OS'] = 3
    build_config['Store'] = 0    
    build_config['VersionBase'] = VERSION_NO_BASE
    build_config['SvnReversion'] = SVN_VERSION_NO
    build_config['CleanAssetsCache'] = True
    build_config['VersionType'] = VERSION_TYPE
    build_config['BuildDateTime'] = BUILD_TIMESTAMP
    build_config['OutputFolder'] = output_folder
    build_config['DevelopmentMode'] = DEBUG_MODE
    build_config['AppName'] = APP_NAME
    build_config['BuildScenes'] = BUILD_SCENES
    with open(json_path, 'w', encoding='utf-8') as file_obj:
            json.dump(build_config, file_obj, indent=4, ensure_ascii=False)
  • 首先,我们确定这个json文件的路径json_path,也是放到我们的打包输出目录中。
  • 然后,构建一个build_config字典,该字典的key就是参数名,value为参数值。该字典可以直接dump到json文件中。
  • 根据我们上面获取到的jenkins打包参数,来填充字典。需要说明的是,jenkins参数和C#端的打包参数不一定是一一对应的,比如这儿的BUILD_TYPEjenkins参数,对应到C#端则是OS参数,这所以这么做,是因为C#端的参数可能更细致,而Jenkins端为了避免选择太多的参数,而可能将一个参数进行"打包"。比如这儿的BUILD_TYPE,当值是'PC'时,只是打包一个普通的PC版本,因此只是设置C#端的OS为对应的枚举值。当如果我们打包一个Steam商店版本,那么BUILD_TYPE可以设置为Steam,这样对应的C#参数不光是OS,还包含Store参数为Steam商店对应的枚举值。我们给出的例子里面并没有实现Steam商店,因此这儿的Store参数就统一设置为默认值了。总之,思路就是根据业务需求,设计Jenkins端的参数,并转换成C#端的参数,保存到字典中。
  • 将build_config字典输出到json文件中。这儿我们使用了json模块的dump方法,当然首先我们要打开文件,获得文件句柄。dump方法的indent参数大于0时,可以获得有缩进格式的json,方便我们查看,ensure_ascii参数设置为False,可以包含中文字符。
  • 执行Unity批处理模式进行打包

    终于,万事俱备,可以执行Unity命令行打包了。根据之前的介绍,打包被分为多遍,以应对域切换可能造成的level 0崩溃问题。我们这儿由于没有assets编译,因此只分为两遍。

    命令行log输出问题

    由于我们是在一个Python脚本中进行处理所有的构建步骤,而每个步骤我们都会输出log,这些log最终在Jenkins的控制台可以看到。但如果我们使用os.system执行控制台命令,就会阻塞log的输出。因此在执行命令行之前,需要flush一下:

    def main()
        # 接上面
        ############### start build ##########################
        print('Start Unity building, please wait...')
        sys.stdout.flush()
    

    执行Unity命令行

    Unity命令行打包的参数之前已经说明过了,本方案我们只需要传入一个自定义参数,即-configFilePath传入我们刚刚生成的json文件的路径。此外就是Unity需要的参数了,比如-projectPath-logFile。这个logFile参数是Unity自己输出的log文件,这个文件有助于分析打包的过程以及失败原因。且每次执行Unity命令行都要指定一个独立的文件,避免互相覆盖。

    def main()
        # 接上面
        proj_path = WORKSPACE
        log_path_1 = f'{log_path}_1.txt'
        exe_result = os.system(f'"{unity}" -batchmode -nographics -executeMethod BuilderCommandline.Build_SwitchEnv -projectPath {proj_path} -logFile {log_path_1} -configFilePath:{json_path}')	
        if exe_result != 0:
            print(f'call Unity Build_SwitchEnv failed. result={exe_result}')	
            exit(1)
        log_path_2 = f'{log_path}_2.txt'
        exe_result = os.system(f'"{unity}" -batchmode -quit -nographics -executeMethod BuilderCommandline.Build_MakePackage -projectPath {proj_path} -logFile {log_path_2} -configFilePath:{json_path}')	
        if exe_result != 0:
            print(f'call Unity Build_MakePackage failed. result={exe_result}')	
            exit(1)
        print("Build Successful!")
    

    这儿我们对于os.system的返回结果进行分析,如果不为0,则表示Unity打包失败,此时我们需要使用exit(1)退出python构建脚本,这将触发Jenkins构建job的失败,从而发送失败邮件。如果一切正常,我们就可以继续最后的步骤了。

    拷贝版本到共享目录

    打包成功之后,我们往往需要将版本包拷贝到一个共享目录,方便大家去下载测试。这样可以避免Jenkins服务器的访问需求,毕竟这个服务器是比较重要的,应该只有管理员可以访问。这儿直接给出main函数最后的一部分代码:

    def main()
        # 接上面
        ############ copy package to shared folder #############
        if PACKAGE_SHARED_FOLDER=='':
                print("Warning: Shared folder is empty, skip copy to shared.")
                exit(1)
        package_name = f'{APP_NAME}_{VERSION_TYPE}_{BUILD_TIMESTAMP}_{SVN_VERSION_NO}'	
        #TODO: add Store Name here if need    	
        if BUILD_TYPE == 'PC':
                package_name += '.zip'
        elif BUILD_TYPE == 'Android':
                package_name += '.apk'
        package_path = f'{output_folder}/{package_name}'
        package_path = package_path.replace('/','\\')  #xcopy need \
        print(f'Copy package {package_path} to shared folder: {PACKAGE_SHARED_FOLDER}')
        sys.stdout.flush();
        exe_result = os.system(f'net use {PACKAGE_SHARED_FOLDER}  "xxx" /user:"xxx"')
        if exe_result != 0:
                print(f'net use shared folder failed. result={exe_result}')	
                exit(1)
        exe_result = os.system(f'xcopy /s /y {package_path} {PACKAGE_SHARED_FOLDER}')
        if exe_result != 0:
                print(f'xcopy to shared folder failed. result={exe_result}')	
                exit(1)
        print('Copy package to shared folder successful! All done!')
    

    共享文件夹的位置也是在Jenkins里面配置的,我们需要使用net use命令去获取该文件夹的权限,这儿需要使用用户名和密码。然后使用xcopy命令进行拷贝。如果成功,python脚本就正常退出,Jenkins那边就会判断构建成功,发送成功邮件。

    本方案到这儿就总结完毕了。但实际上,还是有可以改进和扩展的地方。比如包名称,现在是在C#和python里面分别拼出来的,这其实应该在Jenkins参数里面给出拼接规则,然后从配置文件传入到C#里面。这样更灵活也避免出错。另外打包之后,现在只是简单的copy到局域网一个共享目录中。对于apk或者ipa来说,最好再实现一个OTA服务器,直接将版本包自动部署到OTA服务器上,在邮件中给出OTA下载链接,这样就可以直接下载安装了。