Skip to content

sitting-duck/unity-devops-jenkins

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

unity-devops-jenkins

Jenkins Devops Pipelines for Unity Games

Unity Version: 2021.1.16f1
Jenkins Version: 2.289.3

NothingGame

NothingGame is a Unity Game with nothing in it. The game that does nothing! I just use it as a placeholder in my devops pipeline for Unity Games.

Steps

Step 0: Install DotNet SDK

Install the latest version of DotNet SDK, as of now, that version is 6.0.

Go to the download page [2] and click the “Download .NET SDK x64” button.
image

Double click the installer in your Downloads folder and run it.
image

image

image

image

If your install was successful, you should see this helpful text on the final page:

The installation was successful.
The following products were installed at: 'C:\Program Files\dotnet'
• .NET SDK 6.0.201
• .NET Runtime 6.0.3
• ASP.NET Core Runtime 6.0.3
• .NET Windows Desktop Runtime 6.0.3
This product collects usage data
• More information and opt-out https://aka.ms/dotnet-cli-telemetry
Resources
• .NET Documentation https://aka.ms/dotnet-docs
• SDK Documentation https://aka.ms/dotnet-sdk-docs
• Release Notes https://aka.ms/dotnet6-release-notes
• Tutorials https://aka.ms/dotnet-tutorials

Check your command line and make sure you can call dotnet.

Enter

% dotnet --version

and you should get some output. Mine says:

6.0.201

If you can’t access dotnet from the command line add:

C:\Program Files\dotnet

to your System Path.

Step 1: Add DotNet bin dir to Your System Path

Search for “Environment Variables” in Windows search and select “Edit the system environment variables”
image

Click the Environment Variables button in the System Properties panel
image

In the System Variables table (the bottom one), select the “Path” variable and click the “Edit …” button
image

Click the New button to add C:\Program Files\dotnet to the end of the System Path
image

Refresh your terminal after adding the path to dotnet.exe to the system path and see if you can call

dotnet --version

You will need the dotnet command line interface in the next step.

Step 2: Create System Environment Variables

Create two system environment variables.

Variable 1: the path to Unity.

image

Search for Environment in the Windows search bar

image

Click Environment Variables button

image

Select the New … button under System Variables

image

Give it a meaningful name and paste in the path to the Unity version you are using to build your game for the value. The reason why you are doing is so each build server can set it's own environment variable for the path to Unity, making this Jenkins file reusable. If your new system variable isn't getting picked up by Jenkins you may just need to restart Jenkins. You can do so by going to http://localhost:8080/restart in your browser.

image

Variable 2: The Path to DotNet
image

Click the New… button again to create another System Variable.

image

Give it a meaningful name and paste the path to dotnet.exe for the version of dotnet that you are using for your game in Unity.

You can see what dotnet version you are using in your Unity Game by opening it in Unity and selecting File-> Build Settings …

image

and in the Build Settings window select the Player Settings button.

image

Scroll down until you see Api Compatibility Level* and there is your dotnet version.

Step 3: Unity Setup

For my tutorial I've made a small game that does nothing called the NothingGame.

You can open it in Unity Hub to detect what version of Unity it was built with, or else build your own and use whatever version of Unity you want.

The very first line of your Jenkins file is going to look like this:

UNITY_PATH = '%unity_nothing_game%' // create system environment var pointing to unity install path

and basically that's creating a Groovy variable from '%unity_nothing_game%'

Those % characters (pronounced 'modulo') are basically dereferencing a Windows system variable named 'unity_nothing_game', so we need to create that variable.

I tend to use system variables instead of hard-coding a path into the Jenkins file so that this Jenkins file is re-usable, and in theory another build server could have Unity installed wherever and still use this Jenkins file.

Open the NothingGame in Unity Hub:

image

You may see a warning like this:

image

If you do, go to this page: Unity Download Archive [4]
Click the 2021.x tab

image

You can search for 16 to find this one:

image

Don't worry about finding one with f1 at the end.

Click the green Unity Hub button. It should open up the correct install page in Unity Hub itself.

Allow your browser to open Unity Hub if you need to:

image

Note: you will need Visual Studio 2019 installed, you can see on my screen here it indicates I already have it installed.

image

Make sure you have checked to install "Windows Build Support"

image

Click the blue install button and watch the progress bar:

image

image

Step 3: Creating the System Variable with the Unity Path

image

Click the Environment Variables button

image

Click the "New" button iunder the System Variables table. We're about to create a new system variable.

image

My Unity is installed to this path:

C:\Program Files\Unity\Hub\Editor\2021.1.16f1\Editor

You are going to enter the path to the correct Unity.exe basically in this system variable "unity_nothing_game" if your folder doesn't have Unity.exe in it, it is not the correct folder:

image

Enter that path in the text field:

image

Append Unity.exe to the end of the path to make a complete path to the executable. This is convenient for calling Unity from the command line in the Jenkins file later. [not optional]

image

Make sure Unity.exe is at the end of the environment variable. We are going to call Unity.exe from the command line in our Jenkins file.

Step 4: Create a Jenkins file

Create a Jenkins file like this:

UNITY_PATH = '%unity_nothing_game%' // create system environment var pointing to unity install path

pipeline {
    parameters {
        choice(name: 'build', choices: ['Release', 'Debug'], description: "Release or Debug. Debug Builds take longer")
        choice(name: 'release', choices: ['alpha', 'beta', 'release'], description: "alpha - risky builds that may crash, beta - more mature builds testing before release release - builds ready for deployment to user")    
    }

    agent any 
    
    environment {
        appname = "NothingGame" // Set to your own game. "NothingGame: The game that does nothing!"
        release_name = "${ "${release}" == "alpha" || "${release}" == "beta" ? "${release}" : "" }" 
        target = "${ "${build}" == "Release" ? "${appname}${release_name}.exe" : " ${appname}_Debug_${release_name}.exe" }" // append debug for debug builds, nothing for release builds
    }

    stages {
        stage ('Build') {
        steps { script {
            bat """
            \"${UNITY_PATH}\" -nographics -buildTarget Win64 -quit -batchmode -projectPath . -appname ${appname} -executeMethod Jenkins.CmdLine.parseCommandLineArgs ${build} -buildWindows64Player "${target}"
            """
        }}}
        
    } // end stages
}

Step 5: Create a Command Line Parser inside the Unity with a C# Script

Inside Assets/Editor Create a C# script inside the Unity Editor.

image

Use this text:

#if UNITY_STANDALONE_WIN

using UnityEngine;
using System;
using UnityEditor;
using UnityEditor.Build.Reporting;
using System.Linq;
using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic;

/* Works with Unity version 2021.16f1 */

namespace Jenkins {

    public class CmdLine : MonoBehaviour 
    {

        [DllImport("user32.dll", EntryPoint = "SetWindowText")]
        public static extern bool SetWindowText(System.IntPtr hwnd, System.String lpString);
        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        public static extern System.IntPtr FindWindow(System.String className, System.String windowName);        

        static BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();        

        public static void parseCommandLineArgs() {
            string programName = "NothingGame"; // hardcoded here, but can also be passed in with -appname argument

            string[] args = System.Environment.GetCommandLineArgs();
            int i = 0;
            
            foreach (string arg in args)
            {
                if (i > 0 && arg == "Release") {
                    buildPlayerOptions.options = BuildOptions.None;
                }
                if (i > 0 && arg == "Debug") {
                    buildPlayerOptions.options = BuildOptions.Development | BuildOptions.AllowDebugging | BuildOptions.EnableCodeCoverage | BuildOptions.EnableDeepProfilingSupport;
                    PlayerSettings.productName = PlayerSettings.productName + "_Debug";
                    programName = "_Debug.exe";
                    ChangeTitle(programName);

                    EditorUserBuildSettings.development = true;
                    EditorUserBuildSettings.allowDebugging = true;
                    EditorUserBuildSettings.connectProfiler = true;
                }
                if (i > 0 && arg == "-appname") {
                    programName = args[i+1];
                }
                i++;            
            }
            BuildReport report = BuildPipeline.BuildPlayer(EnabledLevels(), programName, BuildTarget.StandaloneWindows, buildPlayerOptions.options);
            //BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
            BuildSummary summary = report.summary;

            if(summary.result == BuildResult.Succeeded) {
                Debug.Log("Build Succeeded: " + summary.totalSize + " bytes");
            }

            if(summary.result == BuildResult.Failed) {
                Debug.Log("Build Failed");
            }
        }

        // Changes the title bar, often used to visually indicate a game is a debug build
        public static void ChangeTitle(string newTitle) {
            var windowPtr = FindWindow(null, newTitle);
            SetWindowText(windowPtr, newTitle);            
        }    

        private static string[] EnabledLevels() {
            List<string> scenes = new List<string>();
            foreach(EditorBuildSettingsScene scene in EditorBuildSettings.scenes) {
                if(scene.enabled) {
                    scenes.Add(scene.path);
                }
            }
            return scenes.ToArray();
        }
    
    }
}

#endif

Replace "NothingGame" with the name of your Unity game.

This will append "_debug" to the program name in the title bar, making it really obvious if you have shipped the debug build on accident and so on.

Step 6: Create Jenkins Environment Variables

In the main dashboard (go to http://localhost:8080 if you get lost to go back to the main dashboard) and select "Manage Jenkins"

image

Select "Configure System"

image

Scroll down to the section Labelled "Global properties" and check the box labelled "Environment Variables"

image

Click the Add button

image

Add a variable with the name "unity_nothing_game" set to the value:

C:\Program Files\Unity\Hub\Editor\2021.1.16f1\Editor

or wherever you had your Unity installed on your machine

image

Click the Save button at the bottom to accept.

image

Step 6: Create Your Jenkins Project

Click the New Item button in the main dashboard to create your project

image

Give it a project name

image

Select Pipeline for project type.

image

In the General Section (the first section) check the GitHub Project checkbox and enter the url of the Github project:

image

Basically use the same path you use when you do git clone to pull down the repo.

image

Scroll down the Build Triggers Section and Check the "Github hook trigger for GITScm Polling" checkbox. This basically just means that it will build every time you push to the branch that you have it set to watch.

image

Scroll down to the Pipeline Section.
By default it will look like this:

image

Use the ComboBox and switch from "Pipeline Script" to "Pipeline Script from SCM" SCM means source control management by the way.

image

Some new ComboBoxes will appear after that.
In the SCM ComboBox that appears select "Git" instead of None for SCM. You are saying that on Git there will be a "Pipeline Script"

image

For Repository URL paste in that https:// url to the github repo again

https://github.com/sitting-duck/unity-devops-jenkins.git

image

You can skip the "Credentials" section since this is a public repo.

By default the "Branch Specifier" is set to "master", change that to "main"

image

after your change it will look like this:

image

The next thing is "Script Path" and by default it just says Jenkinsfile. You can set the path to the Jenkinsfile here basically. Just leave it at the default, but if you needed to you could set a custom path like "deploy/Jenkinsfile" or have two Jenkinsfiles if you wanted and specify one build server to look for Jenkinsfile-win and the other to Jenkinsfile-mac and things like that if you wanted to.

image

Uncheck Lightweight Checkout

image

I haven't set anything with lightweight checkout in my git commits so you won't need that to checkout and build the NothingGame.

Click the Save button to accept these Settings.

image

Click the Build Now button

image

If it fails the first time with an error like this:

groovy.lang.MissingPropertyException: No such property: build for class: groovy.lang.Binding

That's just because the first time it parses the Jenkinsfile it will choke on the parameters the first time, but it will work every time after that.
Go ahead and build it again. You can see that the Build button says "Build with Parameters" now, so it will work with the parameters it finds in the Jenkinsfile now,

image

You can leave the default parameters and click the build button:
image

View the Console Output by using the Triangle Menu next to the timestamp for the latest build and selecting Console Output

image

If You have Error Message During Build

Scroll past this section if you can build with no errors

DisplayProgressNotification: Build Failed
'' is an incorrect path for a scene file. BuildPlayer expects paths relative to the project folder.

Solution: Add scenes to build in the BuildSettings. BuildSettings had no scenes to build.

image

After You're Done Building

You should go into your Jenkins workspace for that project and check that your game executable is actually there and that it actually runs. Jenkins will show you "Success" at the end of a build if the script did not throw any errors, but Jenkins is not intelligent enough to know if your game actually built the way that you wanted, so you must do some testing and verify.

image

Sometimes this can be misleading. Check your workspace and make sure your build artifacts are actually there

Step 8: Check and Test Your Game

I can see my game is actually here in my workspace:
image

And I double click the game to make sure it actually launches.

image

And it does.

My Tutorials Online:

How to Jenkins Pipeline for Unity Game with SonarQube
https://ashley-tharp.medium.com/how-to-create-a-jenkins-pipeline-to-build-a-unity-game-on-windows-and-analyze-code-quality-on-build-1cda04ac7cbe

Installing SonarQube as a Service on Boot on Windows 10/11
https://ashley-tharp.medium.com/how-to-install-sonarqube-on-windows-11-7361a26ca042

Installing Jenkins on Windows 10/11
https://ashley-tharp.medium.com/how-to-setup-jenkins-on-windows-10-ac969ee921f2

References

[1] Generating and Using a Login Token in SonarQube. https://docs.sonarqube.org/latest/user-guide/user-token/
[2] DotNet 6.0 SDK Download Page Windows. https://dotnet.microsoft.com/en-us/download#windowsvs2015
[3] SonarScanner for MSBuild. https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-msbuild/
[4] Unity Download Archive. https://unity3d.com/get-unity/download/archive

About

Jenkins Devops Pipelines for Unity Games

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages