Skip to content

Unity Pipeline

timrademaker edited this page Dec 15, 2020 · 15 revisions

Example Pipeline Script

A pipeline for unity that runs the project's tests, builds the project, deploys the build to Steam and notifies your team of the pipeline's result through a Discord webhook.

Run the pipeline once after creating it to set up the parameters.

Pipeline Script

library(identifier: 'JenkinsPipelineUtilities@master', retriever: modernSCM([$class: 'GitSCMSource', credentialsId: '', remote: 'https://github.com/timrademaker/JenkinsPipelineUtilities.git']))

pipeline {
    parameters {
        // Project
        string(defaultValue: params.BUILD_METHOD ? params.BUILD_METHOD : '', description: 'The method to execute in order to build the project.', name: 'BUILD_METHOD', trim: true)
        string(defaultValue: params.TEST_FILTERS ? params.TEST_FILTERS : '', description: 'The test filters to use when running tests. Separate different filters with a \';\', or leave empty to not run any tests.', name: 'TEST_FILTERS', trim: true)

        // Discord
        credentials(credentialType: 'org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl', defaultValue: params.DISCORD_WEBHOOK ? params.DISCORD_WEBHOOK : '', description: 'The webhook to use to notify the team of build results.', name: 'DISCORD_WEBHOOK', required: false)

        // Shipping
        booleanParam(defaultValue: params.SHOULD_DEPLOY ? params.SHOULD_DEPLOY : false, description: 'True if the build should be deployed.', name: 'SHOULD_DEPLOY')
        booleanParam(defaultValue: params.DEPLOY_UNSTABLE_BUILD ? params.DEPLOY_UNSTABLE_BUILD : false, description: 'True if the build should be deployed even if it is marked as unstable.', name: 'DEPLOY_UNSTABLE_BUILD')
        credentials(credentialType: 'com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl', defaultValue: params.STEAM_CREDS ? "${params.STEAM_CREDS}" : '', description: 'Login credentials for Steam.', name: 'STEAM_CREDS', required: true)
        string(defaultValue: params.STEAM_DEPLOYMENTBRANCH ? params.STEAM_DEPLOYMENTBRANCH : '', description: 'The branch to which this game should automatically be deployed. Can\'t be \'default\'.', name: 'STEAM_DEPLOYMENTBRANCH', trim: true)
        booleanParam(defaultValue: params.IS_PREVIEW ? params.IS_PREVIEW : false, description: 'True if this build is a preview (i.e. should not actually be deployed).', name: 'IS_PREVIEW')
    }
    
    agent {
        node {
            label ""
        }
    }
    
    options {
        timeout(time: 45, unit: 'MINUTES')
    }
    
    environment {
        // Perforce
        P4_CREDENTIALS = 'Tim180112_P4'                 // The ID of the Perforce credentials to use
        P4_WORKSPACE_FORMAT = 'Tim180112-Jenkins-${JOB_BASE_NAME}'
        P4_WORKSPACE_TEMPLATE = 'Tim180112-Jenkins'     // The Perforce workspace to use as template

        // Project settings
        PROJECT_DIRECTORY = 'ExampleProject'            // Project directory, relative to the workspace root

        // Steam
        STEAM_APPID = '1000'                            // The app ID of the game to ship to Steam.
        STEAM_DEPOTID = '1001'                          // The depot ID of the games's depot.

        // Unity base directory
        UNITY_DIRECTORY = 'C:/Program Files/Unity/Hub/Editor/2019.2.16f1';
    }
    
    stages {
        stage('Setup') {
            steps {
                script {
                    p4.init(env.P4_CREDENTIALS, env.P4_WORKSPACE_FORMAT, env.P4_WORKSPACE_TEMPLATE)
                    p4.pull();
                    unity.init(env.UNITY_DIRECTORY);

                    if(params.SHOULD_DEPLOY) {
                        steam.setup();
                    }
                }
            }
        }
        stage('Run Tests') {
            when {
                expression {
                    params.TEST_FILTERS.size() > 0;
                }
            }

            steps {
                script {
                    unity.runTests("${env.WORKSPACE}/${env.PROJECT_DIRECTORY}", 'PlayMode', params.TEST_FILTERS.split(';') as List<String>);
                }
            }
        }
        stage('Build') {
            steps {
                script {
                    unity.execute("${env.WORKSPACE}/${env.PROJECT_DIRECTORY}", params.BUILD_METHOD);
                }
            }
        }
        stage('Deploy') {
            when {
                expression {
                    params.SHOULD_DEPLOY &&
                    (currentBuild.result != 'UNSTABLE' || params.DEPLOY_UNSTABLE_BUILD)
                }
            }
            
            steps {
                script {
                    contentRoot = "${env.WORKSPACE}/out/build";
                    steam.createDepotManifest(env.STEAM_DEPOTID, contentRoot);
                    appManifest = steam.createAppManifest(env.STEAM_APPID, env.STEAM_DEPOTID, contentRoot, "${env.JOB_BASE_NAME}-${env.BUILD_NUMBER}", params.STEAM_DEPLOYMENTBRANCH, "${env.WORKSPACE}/SteamBuild", params.IS_PREVIEW);
                    steam.tryDeploy(params.STEAM_CREDS, appManifest);
                }
            }
            
        }
    }
    
    post {
        success {
            script {
                if(params.DISCORD_WEBHOOK) {
                    withCredentials([string(credentialsId: params.DISCORD_WEBHOOK, variable: 'WEBHOOK_URL')]) {
                        discord.sendEmbed(env.WEBHOOK_URL, "Ran Jenkins Pipeline for ${env.JOB_BASE_NAME}", "[Build #${env.BUILD_NUMBER}](${env.BUILD_URL})", '3066993', [['**Build Result**', ':white_check_mark: Build succeeded!']]);
                    }
                }
            }
        }
        
        unsuccessful {
            script {
                if(params.DISCORD_WEBHOOK) {
                    withCredentials([string(credentialsId: params.DISCORD_WEBHOOK, variable: 'WEBHOOK_URL')]) {
                        discord.sendEmbed(env.WEBHOOK_URL, "Ran Jenkins Pipeline for ${env.JOB_BASE_NAME}", "[Build #${env.BUILD_NUMBER}](${env.BUILD_URL})", '15158332', [['**Build Result**', ':x: Build failed!']]);
                    }
                }
            }
        }
 
        unstable {
            script {
                if(params.DISCORD_WEBHOOK) {
                    withCredentials([string(credentialsId: params.DISCORD_WEBHOOK, variable: 'WEBHOOK_URL')]) {
                        discord.sendEmbed(env.WEBHOOK_URL, "Ran Jenkins Pipeline for ${env.JOB_BASE_NAME}", "[Build #${env.BUILD_NUMBER}](${env.BUILD_URL})", '16776960', [['**Build Result**', ':warning: Build unstable!']]);
                    }
                }
            }
        }
        
        always {
            script {
                log.parse();
            }
        }

        cleanup {
            script {
                file.delete("${env.WORKSPACE}/out");
                file.delete("${env.WORKSPACE}/temp");
            }
        }
    }
}

Build Method

The build method used by the pipeline (BUILD_METHOD parameter) could look something like this:

using UnityEditor;

public class ScriptBatch
{
    public static void BuildGame()
    {
        string path = "../out/build";
        string[] levels = new string[] { "Assets/Resources/TestMap.unity" };

        BuildPlayerOptions options = new BuildPlayerOptions();
        options.locationPathName = path + "/Game.exe";
        options.scenes = levels;
        options.target = BuildTarget.StandaloneWindows64;
        BuildPipeline.BuildPlayer(options);
    }
}

Using this would be done by setting BUILD_METHOD to "ScriptBatch.BuildGame".

Clone this wiki locally