【Unity】搭建Jenkins打包工作流,远程打热更、构建App

news2024/11/18 6:48:47

Jenkins是团队协作项目打包常用的工作流,不多做介绍。

Jenkins的部署Unity打包环境还是非常简单的:

工作流程如下:

1. 在Jenkins中添加打包配置参数(如: 版本号, 目标平台等), 参数将以UI的形式显示在Jenkins Web界面以便打包前填写参数;

2. 用.bat批处理代码将步骤1的参数保存到json文件,以便打包时获取参数;

3. 用.bat批处理代码调用Unity.exe触发指定C#静态方法,在此方法中执行参数配置和打包逻辑:

Unity.exe -quit -nographics -batchmode -projectPath "%ProjectRoot%" -executeMethod UGF.EditorTools.JenkinsBuilder.BuildApp -logFile "%ProjectRoot%/Tools/Jenkins/UnityBuildLog.log"

具体配置:

1. 下载Jenkins, Jenkins下载链接:Jenkins download and deployment

为了跨平台方便最好选择下载.war格式的Java包;

2. 当前版本的Jenkins要求的JDK版本是11 - 17, 安装后配置JAVA_HOME环境变量(详情自查Java环境安装)

3. 用命令行启动Jenkins:

java -jar jenkins.war

4. Jenkins启动后,在浏览器输入ip地址和端口打开Jenkins Web界面:

如果是本机通过地址 localhost:8080 打开Web界面。

5. 创建Jenkins 打包热更资源和打包App两个任务:

①点击新建视图:

 ②填写视图信息创建视图:

 视图就相当于文件夹,是为了更好的归类。选择对应的视图就可以在视图下新建Item,即任务;

③ 点击新建Item,创建Freestyle project任务:

 ④ 选择建好的任务,进入配置界面就可以为打包添加各种变量和批处理执行流程了:

 ⑤ 勾选This project is parameterized后就可以添加各种变量UI,这些变量用于传递给打包属性,比如版本号、打包目标平台等

 点击添加参数,以添加的参数会以可交互UI的形式显示在Web界面,一遍打包前填写配置参数:

 6. 创建好参数后点击Build with Parameters,就可以填写自定义的打包参数

 7. 使用.bat批处理代码获取打包参数并生成参数的json文件:

进入配置界面,找到Build Steps栏,添加构建步骤,类型选择Execute Windows batch command(Windows批处理脚本),Mac或Linux平台可以选择shell脚本。

 可以直接贴入批处理命令行,也可以将命令行写入.bat文件中,然后直接填写要执行的.bat文件名;

通过%参数名%的形式获取参数值;将参数值写入json文件代码如下:

生成打热更资源的参数json文件:

echo {"ResourceOutputDir":"%ResourceOutputDir%","Platform":"%Platform%","ForceRebuild":%ForceRebuild%,"ResourceVersion":%ResourceVersion%,"UpdatePrefixUrl":"%UpdatePrefixUrl%","ApplicableVersions":"%ApplicableVersions%","ForceUpdate":%ForceUpdate%,"AppUpdateUrl":"%AppUpdateUrl%","AppUpdateDescription":"%AppUpdateDescription%"}>"%ProjectRoot%\Tools\Jenkins\BuildResourceConfig.json"

生成打App的参数json文件:

echo {"ResourceOutputDir":"%ResourceOutputDir%","Platform":"%Platform%","FullBuild":%FullBuild%,"DebugMode":%DebugMode%,"DevelopmentBuild":%DevelopmentBuild%,"BuildAppBundle":%BuildAppBundle%,"Version":"%Version%","VersionCode":%VersionCode%}>%ProjectRoot%\Tools\Jenkins\BuildAppConfig.json

需要注意的是,字符串参数不能有换行,否则.bat批处理代码会执行异常。

8. 使用命令行调用Unity.exe打包:

可以直接用如下方式,使用批处理调用Unity.exe打包,但是此方式Jenkins无法实时在网页显示打包的Log信息:

Unity.exe -quit -nographics -batchmode -projectPath "%ProjectRoot%" -executeMethod UGF.EditorTools.JenkinsBuilder.BuildApp -logFile "%ProjectRoot%/Tools/Jenkins/UnityBuildLog.log"

最好的选择是安装Jenkins的Unity支持插件,使用插件启动Unity打包可以完美在网页输出Log日志。

① 安装Unity插件

进入Jenkins管理界面:

 进入插件管理界面:

 选择Available plugins栏目,搜索安装Unity3d plugin

②  安装Unity3d plugin后,进入Global Tool Configuration配置打包所用的Unity安装路径

 

 ③ 再次进入Item配置,添加启动Unity的Build Steps:

 启动参数为:

-quit -nographics -batchmode -projectPath "${ProjectRoot}" -executeMethod UGF.EditorTools.JenkinsBuilder.BuildResource -logFile "${ProjectRoot}/Tools/Jenkins/UnityBuildLog.log"

注意,注意Unity3d plugins的获取打包参数的方式与批处理脚本不同,使用${参数名}的方式获取参数值;

其中 -nographics 即不显示Unity图形界面,这样会减少内存消耗,一定程度上能提高打包速度。

 -projectPath "${ProjectRoot}" 为配置项目路径;

-executeMethod UGF.EditorTools.JenkinsBuilder.BuildResource为指定启动Unity后自动执行脚本UGF.EditorTools.JenkinsBuilder的BuildResource()静态方法。

在BuildResource中编写打包逻辑,如先获取生成的json参数文件,根据参数初始化各项设置,然后再打包资源。

9. 打包逻辑:

这里偷懒直接使用之前写过的一个编辑器扩展窗口类AppBuildEditor:【Unity编辑器扩展】扩展Unity工具栏Toolbar, 一键出包/打热更简化打包流程_unitytoolbar_TopGames的博客-CSDN博客

 直接调用编辑器中的方法一键打热更资源,一键打App;

需要注意的是,使用命令行打包并不是启动Unity Editor,所以继承EditorWindow的脚本都不会执行Update和OnGUI方法。如果在Update和OnGUI中有初始化参数的逻辑,需要在Jenkins打包函数中做一遍初始化操作。

using System;
using System.IO;
using UnityEditor;
using UnityEngine;

namespace UGF.EditorTools
{
    public class JenkinsBuilder
    {
        const string BuildResourceConfigFile = "Tools/Jenkins/BuildResourceConfig.json";
        const string BuildAppConfigFile = "Tools/Jenkins/BuildAppConfig.json";
        public static void BuildResource()
        {
            Debug.Log("------------------------------Start BuildResource------------------------------");
            var configFile = UtilityBuiltin.ResPath.GetCombinePath(Directory.GetParent(Application.dataPath).FullName, BuildResourceConfigFile);
            if (!File.Exists(configFile))
            {
                Debug.LogError($"构建失败! 构建配置文件不存在:{configFile}");
                return;
            }
            JenkinsBuildResourceConfig configJson = null;
            try
            {
                var jsonStr = File.ReadAllText(configFile);
                configJson = UtilityBuiltin.Json.ToObject<JenkinsBuildResourceConfig>(jsonStr);

            }
            catch (Exception err)
            {
                Debug.LogError($"构建失败! 构建配置文件解析失败:{configFile}, Error:{err.Message}");
                return;
            }
            if (configJson == null)
            {
                Debug.LogError($"构建失败! 反序列构建配置参数失败:{configFile}");
                return;
            }
            
            if (!CheckAndSwitchPlatform(configJson.Platform))
            {
                Debug.LogError($"构建失败! 切换平台({configJson.Platform})失败.");
                return;
            }

            var appBuilder = EditorWindow.GetWindow<AppBuildEidtor>();
            appBuilder.Show();
            appBuilder.JenkinsBuildResource(configJson);
        }
        public static void BuildApp()
        {
            Debug.Log("------------------------------Start BuildApp------------------------------");
            var configFile = UtilityBuiltin.ResPath.GetCombinePath(Directory.GetParent(Application.dataPath).FullName, BuildAppConfigFile);
            if (!File.Exists(configFile))
            {
                Debug.LogError($"构建失败! 构建配置文件不存在:{configFile}");
                return;
            }
            JenkinsBuildAppConfig configJson = null;
            try
            {
                var jsonStr = File.ReadAllText(configFile);
                configJson = UtilityBuiltin.Json.ToObject<JenkinsBuildAppConfig>(jsonStr);

            }
            catch (Exception err)
            {
                Debug.LogError($"构建失败! 构建配置文件解析失败:{configFile}, Error:{err.Message}");
                return;
            }
            if (configJson == null)
            {
                Debug.LogError($"构建失败! 反序列换构建配置失败:{configFile}");
                return;
            }
            if (!CheckAndSwitchPlatform(configJson.Platform))
            {
                Debug.LogError($"构建失败! 切换平台{configJson.Platform}失败.");
                return;
            }
            var appBuilder = EditorWindow.GetWindow<AppBuildEidtor>();
            appBuilder.Show();
            appBuilder.JenkinsBuildApp(configJson);
        }
        /// <summary>
        /// 切换到目标平台
        /// </summary>
        /// <param name="platform"></param>
        /// <returns></returns>
        private static bool CheckAndSwitchPlatform(BuildTarget platform)
        {
            if (EditorUserBuildSettings.activeBuildTarget != platform)
            {
                BuildTargetGroup buildTargetGroup = BuildPipeline.GetBuildTargetGroup(platform);
                Debug.Log($"#########切换平台,TargetGroup:{buildTargetGroup}, BuildTarget:{platform}#######");
                return EditorUserBuildSettings.SwitchActiveBuildTarget(buildTargetGroup, platform);
            }
            return true;
        }
    }

    public class JenkinsBuildResourceConfig
    {
        public string ResourceOutputDir; //构建资源输出目录
        public BuildTarget Platform; //构建平台
        public bool ForceRebuild; //强制重新构建全部资源
        public int ResourceVersion; //资源版本号

        public string UpdatePrefixUrl; //热更资源服务器地址
        public string ApplicableVersions; //资源适用的App版本号
        public bool ForceUpdate; //是否强制更新App
        public string AppUpdateUrl; //App更新地址
        public string AppUpdateDescription; //App更新说明
    }
    public class JenkinsBuildAppConfig
    {
        public string ResourceOutputDir; //构建资源输出目录(只有非全热更需要)
        public BuildTarget Platform; //构建平台
        public bool FullBuild; //打包前先为热更生成AOT dll
        public bool DebugMode; //显示debug窗口
        public bool DevelopmentBuild; //调试模式
        public bool BuildAppBundle; //打Google Play aab包
        public string Version; //App版本号
        public int VersionCode; //App版本编号
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/496447.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器人抓取检测——Dex-Net

如今&#xff0c;在各种期刊顶会都能看到平面抓取检测的论文&#xff0c;他们声称能应对多物体堆叠场景&#xff0c;然而实际效果都不尽人意&#xff0c;我认为主要原因有如下几点&#xff1a; 缺乏多物体堆叠场景的抓取数据集。现在最常用的Cornell Grasp Dataset, Jacquard数…

政务网中使用内部华为云

项目按甲方要求&#xff0c;部署在政务网&#xff0c;各种需要在系统中播放的视频存放于内部华为云&#xff1b;然后&#xff0c;系统需要在互联网上访问。 经过一天捣鼓&#xff0c;终于搞定。过程中遇到了许多问题&#xff0c;有nginx代理的&#xff0c;docker域名解析的&am…

FTP Entering Extended Passive Mode

目录 原因 两种方法解决,哪个行用哪种 方法一 方法二 原因 FTP的连接建立有两种模式PORT

10个优秀设计网站盘点

从平面广告设计、包装设计和标志设计到游戏特效&#xff0c;都与我们的生活息息相关。过去&#xff0c;设计师依靠一张图纸和一支笔&#xff0c;但进入数字时代后&#xff0c;设计工作从图纸转移到了电脑上。 各种设计网站和在线设计工具相继衍生&#xff0c;简化了工作步骤&a…

Packet Tracer - 配置扩展 ACL - 场景 1

Packet Tracer - 配置扩展 ACL - 场景 1 拓扑图 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 R1 G0/0 172.22.34.65 255.255.255.224 不适用 G0/1 172.22.34.97 255.255.255.240 不适用 G0/2 172.22.34.1 255.255.255.192 不适用 服务器 NIC 172.22.…

戴尔Alienware x15R1 x15R2原厂win11系统带F12 Support Assist OS Recovery恢复功能

戴尔Alienware x15R1 x15R2原厂win11系统带F12 Support Assist OS Recovery恢复功能 恢复各机型预装系统&#xff0c;带所有dell主题壁纸、dell软件驱动、带戴尔SupportAssist OS Recovery恢复功能&#xff0c;一次性恢复成新机状态&#xff0c;并且以后不用重装系统&#xff…

pyinstaller打包Mediapipe时遇到的问题

使用pyinstaller对python文件打包 打包流程 安装pyinstaller pip install pyinstaller打包文件 pyinstaller test.py 打包完成后会生成一个dist文件夹,打包的文件会在里面,找到test.exe。 pyinstaller -F test.py 加上-F会把所有的文件打包成一个exe,也是在dist文件夹下…

Docker File

DockerFile 是用来构建Docker镜像的构建文件&#xff0c;是由一些列命令和参数构成的脚本。 一、DockerFile 一、在home目录下创建docker-test-volume目录 cd /home mkdir docker-test-volume 二、在home目录下的docker-test-volume目录创建dockerfile1文件 vim dockerfile1…

运营-8.内容分发

内容分发本质要解决的问题包含两点&#xff1a; 1.高效的连接人与信息 2.过滤出有价值的信息&#xff0c;让合适的人看到合适的信息。 常见的内容分发方式 1.编辑分发 2.订阅分发 3.社交分发 4.算法分发 TIPS&#xff1a;根据产品性质、技术实力等因素&#xff0c;不同…

长尾学习(一):Long-Tail Learning via Logit Adjustment

一、背景 这是一篇从损失函数入手解决长尾问题的一种新思路&#xff0c;借鉴基于标签频次的logit adjustment方法&#xff0c;鼓励模型在高频类别与低频类别之间的Margin较大&#xff0c;提出了两种校准方法&#xff1a; 事后校准&#xff08;post-hoc adjustment&#xff09;…

tiechui_lesson03_缓冲读写与自定义控制

学习了与应用层通过缓冲区方式的交互&#xff0c;包括读写&#xff0c;自定义控制等。小坑比较多&#xff0c;大部分是是头文件和设置上的错误&#xff0c;跟着视频敲想快进就跳过了一些细节。包括&#xff1a; <windef.h> 头文件的引用 //使用DWORD等类型switch语句…

iOS开发多target

场景 背景:设想一下有一个场景,一个业务分为多种身份,他们大部分功能是相同的,但是也有自己的差异性。这种情况,想要构建出不同身份的APP。你会怎么做??? 当然,你可以拷贝一份代码出来,给项目重新命名。这样做的好处是,他们互相不会冲突,不用去关心是否有逻辑的冲…

Python中变量赋值过程的理解

Python中变量赋值过程的理解 在Python中对变量赋值过程的理解&#xff0c;有助于学习者对Python的变量和所指向的对象之间的指向关系深刻理解&#xff0c;避免编程中多个变量赋值后&#xff0c;对变量结果的不确定&#xff0c;减少赋值过程中疑问和困惑。 1.赋值过程基本过程 …

全文检索-Elasticsearch-进阶检索

文章目录 前言一、SearchAPI1.1 URL 后接参数检索1.2 URL 加请求体检索 二、Query DSL2.1 基本语法格式2.2 匹配查询 match2.3 短语匹配 match_phase2.4 多字段匹配 multi_match2.5 复合查询 bool2.6 过滤 filter2.7 查询 term2.8 聚合 aggregations 三、Mapping3.1 待完成3.2 …

Mybatis动态SQL用法

动态SQL是Mybatis的一大重要特性&#xff0c;它可以完成不同条件下的SQL拼接&#xff0c;降低了因为SQL语句书写中的小错误而造成程序报错的概率&#xff0c;例如拼接时要确保不能忘记添加必要的空格&#xff0c;还要注意去掉列表最后一个列名的逗号&#xff0c;利用动态SQL就可…

Vue项目dialog组件数据项清空

目录 一、重置myParams 二、this.$refs["name"].resetFields() 1、使用v-if 然后渲染, 无效 2、使用nextTick, 数据显示慢一拍, 重新打开dialog才会显示上次输入的内容 三、复盘-最终方法 一、重置myParams <el-form:model"myParams":rules&quo…

ssh登录出现Permission denied, please try again可能的解决方案

问题描述 环境&#xff1a;ubuntu 20.04问题描述&#xff1a;在已经设置免密登录后&#xff0c;ssh登录时&#xff08;例如ssh localhost&#xff09;还需要输入密码&#xff0c;并且输入密码后出现下面一系列的报错&#xff1a; xxx(用户名)localhosts password: Permissio…

Makefile教程(入门介绍)

文章目录 前言一、Makefile介绍二、make和Makefile的关系三、编写一个简单的Makefile总结 前言 本篇文章将带大家学习Makefile&#xff0c;Makefile在文件的编译中起到重要作用&#xff0c;在Linux中我们也是经常使用到Makefile&#xff0c;下面我将会带大家学习什么是Makefil…

GNSS定位原理--理解笔记

1、利用“后方交会”原理进行定位。 由已知3个点的坐标以及3个已知点到未知点的距离&#xff0c;就可以计算出未知点的坐标。测量出3个已知点到位置点的距离后&#xff0c;根据三球交会定位&#xff0c;利用方程计算出未知点坐标。 两球交会得到一个圆&#xff0c;三球交会得…

【自学网络安全】从零开始学习网络渗透的核心知识点,助你入门宝典

前言 上周旁听了一个大学学长组织的线上网络安全交流会&#xff0c;里边不乏充斥着各位行业大牛&#xff0c;讲的内容确实精彩&#xff0c;可能对于网络安全经验5年的人来说&#xff0c;是受益匪浅&#xff0c;欢迎程度极高&#xff0c;恨不得跳出屏幕来表示赞同&#xff0c;毕…