Skip to content

Unreal Engine Pipeline

timrademaker edited this page Oct 16, 2020 · 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}" : 'Project', 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 for 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 = 'Tim180112-Jenkins'              // The Perforce workspace to use

        // 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.pull(env.P4_CREDENTIALS, env.P4_WORKSPACE, 'jenkins-${JOB_NAME}');
                    ue4.init("${env.UE_DIR}", 'Win64', 'Development');
                }
            }
        }
        stage('Build') {
            when {
                expression {
                    fileExists("${env.WORKSPACE}/${env.PROJECT_DIRECTORY}/Source")
                }
            }
            
            steps {
                script {
                    ue4.build("${env.WORKSPACE}/${env.PROJECT_DIRECTORY}", env.PROJECT_NAME);
                }
            }
        }
        stage('Run Tests') {
            steps {
                script {
                    if(params.TEST_FILTERS) {
                        ue4.runTestsNamed("${env.WORKSPACE}/${env.PROJECT_DIRECTORY}", env.PROJECT_NAME, params.TEST_FILTERS.split(';'));
                    }
                }
            }
        }
        stage('Package') {
            steps {
                script {
                    ue4.package("${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.lower() in ['win64', 'win32']) {
                        platformName = 'Windows';
                    }
                    
                    contentRoot = "${env.BUILD_OUT_DIR}/${platformName}NoEditor";
                    steam.createDepotManifest(params.STEAM_DEPOTID, contentRoot);
                    appManifest = steam.createAppManifest(env.STEAM_APPID, env.STEAM_DEPOTID, contentRoot, "${env.JOB_BASE_NAME}-${env.BUILD_NUMBER}", params.IS_PREVIEW, setLiveOnBranch: params.STEAM_DEPLOYMENTBRANCH);
                    steam.tryDeploy(params.STEAM_CREDS, appManifest);
                }
            }
            
        }
    }
    
    post {
        success {
            script {
                if(params.DISCORD_WEBHOOK) {
                    withCredentials([string(credentialsId: params.DISCORD_WEBHOOK, variable: 'WEBHOOK_URL')]) {
                        discord.sendMessage(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.sendMessage(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.sendMessage(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