【Unity3D】Jenkins Pipeline流水线自动构建Apk

发布于:2025-02-13 ⋅ 阅读:(7) ⋅ 点赞:(0)

目录

一、准备阶段

二、创建Pipeline流水线项目

三、注意事项


一、准备阶段 

1、安装tomcat 10.0.5 

Index of apache-local/tomcat/tomcat-10

2、安装jdk 17

Java Archive Downloads - Java SE 17.0.13 and later

3、下载Jenkins 2.492.1 (.war)包

War Jenkins Packages

将jenkins.war包移动到D:\Program Files\Apache Software Foundation\Tomcat 10.0\webapps下

配置环境变量

CATALINA_HOME
D:\Program Files\Apache Software Foundation\Tomcat 10.0

JAVA_HOME
E:\JDK(保持使用Unity3d所需的JDK 1.8版本)

Path环境变量新增路径
%CATALINA_HOME%\bin

D:\Program Files\Apache Software Foundation\Tomcat 10.0\bin下新建一个setenv.bat文件,tomcat启动jenkins 2.492.1必须使用JDK 17或以上版本(有要求,否则启动会失败)

set JAVA_HOME=D:\Program Files\Java\jdk-17

win + R 输入cmd打开运行命令窗口,输入startup,会执行D:\Program Files\Apache Software Foundation\Tomcat 10.0\bin\startup.bat文件,启动tomcat

可检查上面红框处是否已经指定使用对应的JDK版本,若不是可能构建时也会出各种问题。 

启动Tomcat窗口后,不能关闭,我们就是使用它访问Jenkins网页的,默认是http://localhost:8080/jenkins/

关于Jenkins的密码登录、插件安装可参考,插件:Unity3d、Gradle、Pipeline、Pipeline: Stage View、Git plugin、Push Over SSH、Maven Integration Plugin、Docker Pipeline

【Unity】Jenkins自动打包入门小结_jenkins unity-CSDN博客

安装插件镜像地址(文本使用清华大学维护的镜像中心,可正常安装插件)

维护方    镜像中心地址
Jenkins 中文社区    https://updates.jenkins-zh.cn/update-center.json
清华大学    https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
华为开源镜像站    https://mirrors.huaweicloud.com/jenkins/updates/update-center.json
腾讯    https://mirrors.cloud.tencent.com/jenkins/updates/update-center.json
ustc    https://mirrors.ustc.edu.cn/jenkins/updates/update-center.json
bit    https://mirror.bit.edu.cn/jenkins/updates/update-center.json
lework    https://cdn.jsdelivr.net/gh/lework/jenkins-update-center/updates/tencent/update-center.json https://cdn.jsdelivr.net/gh/lework/jenkins-update-center/updates/tsinghua/update-center.json https://cdn.jsdelivr.net/gh/lework/jenkins-update-center/updates/ustc/update-center.json https://cdn.jsdelivr.net/gh/lework/jenkins-update-center/updates/bit/update-center.json

 

 

修改Jenkins工作区 workspace地址

2个文件:C:\ProgramData\Jenkins\.jenkins\config.xml 和 C:\Users\lenovo\.jenkins\config.xml都修改如下标签,E:/JenkinsFile/workspace是自定义工作区,E:/JenkinsFile/builds是构建记录目录,${ITEM_FULL_NAME}是Jenkins项目名

  <workspaceDir>E:/JenkinsFile/workspace/${ITEM_FULL_NAME}</workspaceDir>
  <buildsDir>E:/JenkinsFile/builds/${ITEM_FULL_NAME}</buildsDir> 

二、创建Pipeline流水线项目

脚本式Pipeline语法(还有个是声明式SCM,需配合Git拉取 具体百度) 

pipeline {
    agent any
 
    environment {
        JAVA_HOME = "E:/JDK" // 确保这里的路径正确,或者使用环境变量如${JAVA_HOME}
    }
    
    tools {
        // 指定JDK版本,例如JDK 1.8
        jdk 'JDK'
    }
    
    //参数设定 参数name、choices要对应上Jenkins设置的参数
    parameters{
        choice(name: "Platform",choices:['Android','Window64'],description: "平台")
        string(name: "BuildPath",defaultValue: "E:/UnityProject/MilkGameFramework_AutoBuildTest/BuildOutput/$Platform/AutoBuildTest", description: "构建输出目录")
    }
    
    stages {
        stage('打包') {
            steps {
                script {
                    //执行一个.sh脚本文件(Shell命令)打包
                    sh "F:/SH/build.sh"
                    echo "打包完成"
                }
            }
        }
    }
}

F:/SH/build.sh文件如下,和打包Windows时一样的调用Unity去执行静态方法传参打包, 只有2个参数Platform和BuildPath,这2个参数是直接由Jenkins Pipeline的参数设置传入的,想增加其他参数同理添加,Pipeline 脚本式语法百度下(Groovy语法)

echo 'execute unity script to build project ' + $Platform + $BuildPath

D:/UnityHubInstallPath/2019.4.0f1/Editor/Unity.exe -quit -batchmode -projectPath E:/UnityProject/MilkGameFramework_AutoBuildTest -executeMethod UnityProjectBuilder.CommandLineBuild -logFile JenkinsBuildUnity.log Platform-$Platform BuildPath-$BuildPath

 配置完成后即可去点击构建(构建之前先把Unity相关的脚本配置好)

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEditor.Build.Reporting;

public class UnityProjectBuilder
{

    static string[] GetBuildScenes()
    {
        List<string> names = new List<string>();

        foreach (EditorBuildSettingsScene e in EditorBuildSettings.scenes)
        {
            if (e == null)
                continue;

            if (e.enabled)
                names.Add(e.path);
        }
        return names.ToArray();
    }

    //测试用
    [MenuItem("BuildPackage/Build(Android)")]
    public static void BuildPackage()
    {
        LocalCommandLineBuild(@"E:\UnityProject\MilkGameFramework_AutoBuildTest\BuildOutput\Android\Test", "Android");
    }

    /// <summary>
    /// 此方法是从jienkins上接受  数据的 方法
    /// </summary>
    static void CommandLineBuild()
    {
        LocalCommandLineBuild(GetJenkinsParameter("BuildPath"), GetJenkinsParameter("Platform"));        
    }

    static void LocalCommandLineBuild(string path, string platform)
    {
        try
        {

            Debug.Log("Command line build\n------------------\n------------------");
            string[] scenes = GetBuildScenes();
            //string path = @"E:\Unity游戏包\Android\消消乐游戏";//这里的路径是打包的路径, 定义
            //string path = GetJenkinsParameter("BuildPath");
            Debug.Log(path);
            for (int i = 0; i < scenes.Length; ++i)
            {
                Debug.Log(string.Format("Scene[{0}]: \"{1}\"", i, scenes[i]));
            }
            // ProjectPackageEditor.BuildByJenkins(GetJenkinsParameter("Platform"), GetJenkinsParameter("AppID"), GetJenkinsParameter("Version"), GetJenkinsParameter("IPAddress"));
            Debug.Log("Starting Build!");
            Debug.Log("Platform: " + GetJenkinsParameter("Platform"));

            //string platform = GetJenkinsParameter("Platform");
            if (platform == "Android")
            {
                Debug.Log("构建安卓开始:" + path + ".apk");
                BuildReport report = BuildPipeline.BuildPlayer(scenes, path + ".apk", BuildTarget.Android, BuildOptions.CompressWithLz4);
                if (report.summary.result == BuildResult.Succeeded)
                {
                    Debug.Log("Build succeeded: " + path + ".apk");
                }
                else if (report.summary.result == BuildResult.Failed)
                {
                    Debug.LogError("Build failed");
                    foreach (var log in report.steps)
                    {
                        if (log.messages != null)
                        {
                            foreach (var message in log.messages)
                            {
                                Debug.LogError(message.content);
                            }
                        }
                    }
                }
            }
            else if (platform == "IOS")
            {
                //BuildPipeline.BuildPlayer(scenes, path, BuildTarget.iOS, BuildOptions.AcceptExternalModificationsToPlayer);
            }
            else if (platform == "Window64")
            {
                BuildReport report = BuildPipeline.BuildPlayer(scenes, path + ".exe", BuildTarget.StandaloneWindows64, BuildOptions.None);
                if (report.summary.result == BuildResult.Succeeded)
                {
                    Debug.Log("Build succeeded: " + path + ".exe");
                }
                else if (report.summary.result == BuildResult.Failed)
                {
                    Debug.LogError("Build failed");
                    foreach (var log in report.steps)
                    {
                        if (log.messages != null)
                        {
                            foreach (var message in log.messages)
                            {
                                Debug.LogError(message.content);
                            }
                        }
                    }
                }
            }
        }
        catch (Exception err)
        {
            Debug.LogError("方法F中捕捉到:" + err.Message);
            throw;//重新抛出当前正在由catch块处理的异常err
        }
        finally
        {
            Debug.Log("---------->  I am copying!   <--------------");
        }
    }


    /// <summary>
    ///解释jekins 传输的参数
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    static string GetJenkinsParameter(string name)
    {
        /*下面是解析这个字符串,用空格分隔,每一个arg是空格分隔后的字符串,然后继续解析出Platform参数和BuildPath参数,
 注意:$BuildPath是我们Jenkins上定义的参数,它会传入一个字符串路径
 -quit -batchmode -projectPath D:\下载文件\游戏蛮牛源码\一个消消乐工程 -executeMethod UnityProjectBuilder.CommandLineBuild -logFile JenkinsBuildUnity.log Platform-$Platform BuildPath-$BuildPath
 */
        foreach (string arg in Environment.GetCommandLineArgs())
        {
            //Debug.Log("arg:" + arg);
            if (arg.StartsWith(name))
            {
                return arg.Split("-"[0])[1];
            }
        }
        return null;
    }
}

等待一会打包成功后会得到

若失败了,去查看Jenkins日志

三、注意事项

1、Pipeline插件有最低Jenkins版本要求,最好是使用最新版本的Jenkins(其他版本可能有问题)

2、Tomcat使用11.0.x版本可能有问题,类似Could not create Java Virtual Machine...,故尝试使用Tomcat 10.0.5版本正常,若有类似问题请更换Tomcat版本(具体原因不清楚)

3、Jenkins版本有对应JDK版本要求,本文Jenkins要求必须JDK 17或以上,否则无法正常启动Jenkins

4、Tomcat启动的Jenkins是查找不到Jenkins服务的。

5、Tomcat换版本后,要注意变更环境变量CATALINA_HOME指定的地址,忘了就还是运行旧的Tomcat,即使你是去到具体的Tomcat双击startup.bat运行的,它只认环境变量。

6、Pipeline Script 语法对\敏感,尽量不要用\,全部改为/

7、点击构建时,可能会立刻报错,此时请把Pipeline代码设置为最简单的案例,例如:

pipeline {
    agent any
    stages {
        stage('打包') {
            steps {
                script {
                    echo "打包测试"
                }
            }
        }
    }
}

若发现还是有报错,类似找不到stages、stage、steps等字眼的,很可能还是Pipeline相关的插件没更新好,要全部更新到最新。

8、一种非常奇怪的bug,具体怎么解决也是稀里糊涂的,反正就是一点击构建,瞬间失败,1ms都没执行,查看build log

报错1:Also:   org.jenkinsci.plugins.workflow.actions.ErrorAction$ErrorId: 3f2d47bc-e83a-4bd4-beae-34a81a0ce08f
groovy.lang.MissingPropertyException: No such property: Platform for class: groovy.lang.Binding
    at groovy.lang.Binding.getVariable(Binding.java:63)
    at PluginClassLoader for script-security//org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(S

报错2:Also:   org.jenkinsci.plugins.workflow.actions.ErrorAction$ErrorId: eddf3d49-e60e-4fd1-aeaf-d230d2946b16
java.lang.NullPointerException: Cannot invoke "java.util.Map.size()" because "map" is null
    at java.base/java.util.TreeMap.putAll(TreeMap.java:314)
    at hudson.slaves.EnvironmentVariablesNodeProperty.buildEnvVars(EnvironmentVariablesNodeProperty.java:87)

jenkins升级后启动job遇到问题java.lang.NullPointerException: Cannot invoke “java.util.Map.size()“ because “map_cannot invoke "java.util.map.size()" because "m" i-CSDN博客

参考如上文章后得知大概是config.xml被我改崩了,因此我直接删了C:\ProgramData\Jenkins\.jenkins\config.xml(记得缓存),然后重启Jenkins,也就是重新关闭tomcat和开启tomcat运行jenkins 去构建一次就会自动生成config.xml,好像还要等它一会儿才会生成,如果还是没生成,可以试试重启电脑,然后正常修改workspace地址即可。之后就能正常打包了,很有可能是我没改<buildsDir>标签,仅改了<workspaceDir>标签问题,也可能是我更换了太多次Jenkins版本,以及也尝试过使用Jenkins.msi安装包形式去安装(安装包形式有Jenkins服务)本文是将所有安装包Jenkins版本卸载,仅使用tomcat+jenkins形式的