Skip to content

Unreal Engine Pipeline

Tim Rademaker edited this page Dec 4, 2022 · 13 revisions

Example Pipeline Script

A pipeline for Unreal Engine that runs the project's tests, builds and packages 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.CONFIGURATION ? params.CONFIGURATION : 'Development', description: 'The build configuration for this project. Options: Development, Shipping.', name: 'CONFIGURATION', trim: true)
        string(defaultValue: params.TARGET_PLATFORM ? params.TARGET_PLATFORM : 'Win64', description: 'The target platform to build the project for. Options: Win32, Win64, HoloLens, Mac, XboxOne, PS4, IOS, Android, Linux, LinuxAArch64, AllDesktop, TVOS, Switch, Quail, Lumin.', name: 'TARGET_PLATFORM', 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 ""
            customWorkspace "C:/Jenkins/${env.JOB_NAME}"
        }
    }
    
    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
        PROJECT_NAME = 'ExampleProject'                 // The name of the project file (without '.uproject')
        PROJECT_DIRECTORY = 'ExampleProject'            // Project directory, relative to the workspace root
        BUILD_OUT_DIR = "${env.WORKSPACE}/out/build"    // The folder in which to output the build

        // 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.

        // Unreal Engine base directory
        UE_DIR = 'C:/Program Files/Epic Games/UE_4.25';
    }
    
    stages {
        stage('Setup') {
            steps {
                script {
                    p4.init(env.P4_CREDENTIALS, env.P4_WORKSPACE_FORMAT, env.P4_WORKSPACE_TEMPLATE)
                    p4.pull();
                    unreal.init(env.UE_DIR, '4', params.TARGET_PLATFORM, params.CONFIGURATION);

                    if(params.SHOULD_DEPLOY) {
                        steam.setup();
                    }
                }
            }
        }
        stage('Build') {
            when {
                expression {
                    fileExists("${env.WORKSPACE}/${env.PROJECT_DIRECTORY}/Source/${env.PROJECT_NAME}.Target.cs")
                }
            }
            
            steps {
                script {
                    unreal.build("${env.WORKSPACE}/${env.PROJECT_DIRECTORY}", env.PROJECT_NAME);
                }
            }
        }
        stage('Run Tests') {
            steps {
                script {
                    if(params.TEST_FILTERS) {
                        unreal.runTestsNamed("${env.WORKSPACE}/${env.PROJECT_DIRECTORY}", env.PROJECT_NAME, params.TEST_FILTERS.split(';') as List<String>);
                    }
                }
            }
        }
        stage('Package') {
            steps {
                script {
                    unreal.packageProject("${env.WORKSPACE}/${env.PROJECT_DIRECTORY}", env.PROJECT_NAME, env.BUILD_OUT_DIR);
                }
            }
        }
        stage('Deploy') {
            when {
                expression {
                    params.SHOULD_DEPLOY &&
                    (currentBuild.result != 'UNSTABLE' || params.DEPLOY_UNSTABLE_BUILD)
                }
            }
            
            steps {
                script {
                    platformName = params.TARGET_PLATFORM;
                    if(params.TARGET_PLATFORM.toLowerCase() in ['win64', 'win32']) {
                        platformName = 'Windows';
                    }
                    
                    contentRoot = "${env.BUILD_OUT_DIR}/${platformName}NoEditor";
                    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");
            }
        }
    }
}
Clone this wiki locally