/jenkins-shared-library-unity

A Jenkins Shared Library with a collection of functions for Unity projects.

Primary LanguageGroovy

Jenkins Shared Library for Unity

Test Suite Mastodon: @johansteen Twitter: @artstorm Discord: Bitbebop

A Jenkins shared library with a collection of pipeline steps and functionality useful when setting up a Jenkins CI pipeline for Unity projects. In addition to Unity specific functionality, there is also a selection of more generic functions and steps that have proven useful when setting up continuous integration for game dev projects.

Available Functions

Unity Specific

Function Summary
unityBuildNumber Get the build number set for the Standalone build in Unity.
unityCodeCoverageReport Runs Unity's Code Coverage reporter.
unityCodeCoverageReportSummary Parses Summary.xml from the Unity generated code coverage report.
unityPublishCodeCoverageHTMLReport Publishes the Unity Code Coverage Report to the job page.
unityTestRunner Uses Unity's Test Runner to execute the test suite.
unityTestRunnerReport Create a report from NUnit tests.
tests.summary Get Junit test result summary.

Generic

Function Summary
getBuildType Retrieves the build type for the current running build.
setJobDisplayName Sets the display name based on the build type.
git.branchName Get the current branch name.
git.commitSha Get the full git commit sha for current commit.
git.commitShaShort Get the short 7 character git commit sha for current commit.
git.getTrailerValue Get the trailer value for the specified token.
github.pullRequestComment Creates or updates the Jenkins bot issue comment for the pull request.
github.ownerRepo Get the owner/repo part from the project's git url.
github.pullRequest Returns the pull request the current commit belongs to.
github.issueComments Retrieves all comments for an issue/pull request.
github.createIssueComment Create issue comment.
github.updateIssueComment Update issue comment.
github.createCheckRun Create a check run.
github.updateCheckRun Update a check run.
influxdb.addMeasurement Add a measurement for writing to InfluxDB.
influxdb.write Write all added measurements to InfluxDB.
timers.start Creates and starts a new timer with the specified name.
timers.stop Stops the timer with the specified name.
timers.getDuration Gets the duration in ms between start and stop for the timer with the specified name.
utils.getDirectorySize Get the size of the provided directory in bytes.
utils.getFileSize Get the size of the provided file in bytes.

Installation

Read the Extending with Shared Libraries > Using Libraries section in Jenkins User Handbook for full instruction how to add a shared library to a Jenkins instance and how to access the functionality from a Jenkinsfile.

When using the library in a Jenkinsfile, use a version specifier, to avoid surprises if future updates of the library introduces breaking changes.

@Library('shared-library@v1.0.4') _

Pipeline Utility Steps

Several functions in this library uses functions from the Jenkins plugin Pipeline Utility Steps. Make sure this plugin is installed in the Jenkins instance using this shared library.

GitHub App

For the GitHub specific steps a GitHub app must be registered to obtain the access token for Jenkins to communicate with the GitHub API. It also requires the Jenkins plugins

Pipeline Steps and Functions

An overview of the pipeline steps and functionality this shared library exposes.

Unity Specific

Unity Build Number

This function parses Unity's Project Settings and returns the build number that has been set for the Standalone build in Unity's Project Settings > Player > Build.

steps {
    echo "Unity build number: ${unityBuildNumber()}"
}

Unity Code Coverage Report

Runs Unity's Code Coverage reporter. This assumes the Unity project has the Code Coverage package installed.

The reporter takes two parameters, unityCodeCoverageReport(assemblyFilters, pathFilters).

post {
    always {
        // Generate Reports from Unit Tests and Code Coverage.
        unityCodeCoverageReport('+Bitbebop.Blitloop,+Dagobah.*,+Cosmos.*', '-**/Assets/Scripts/Input/GameInput.cs')
    }
}

Unity Code Coverage Report Summary

Parses Summary.xml from the Unity generated code coverage report and returns the summary as an object with the summary items as properties. This can then be posted to Discord, Slack, a time series database, or any other destination.

Available properties:

  • coveredLines
  • uncoveredLines
  • coverableLines
  • totalLines
  • lineCoverage
def codeCoverage = unityCodeCoverageReportSummary()
echo  "${codeCoverage.lineCoverage}"

Unity Publish Code Coverage HTML Report

Publishes the Unity Code Coverage Report so it's available from the job page.

post {
    always {
        unityPublishCodeCoverageHTMLReport()
    }
}

Unity Test Runner

Uses Unity's Test Runner to execute the test suite.

steps {
    unityTestRunner("EditMode", "iOS", true, env.CODE_COVERAGE_ASSEMBLY_FILTER, env.CODE_COVERAGE_PATH_FILTERS)
}

Unity Test Runner Report

Creates a merged report from Unitys PlayMode and EditMode test files.

post {
    always {
        unityTestRunnerReport()
    }
}

tests

Parses the results from the Unity test runner and returns the summary as an object. This can then be posted to Discord, Slack, a time series database, or any other destination.

Available properties:

  • total
  • failed
  • skipped
  • passed
def testSummary = tests.summary()
echo  "${testSummary.passed}"

Common

Get Build Type

Retrieves the build type for the current running build based on branch name prefixes. This is useful to rely on branch names to determine the build logic.

  • internal: /feature/*
  • testflight: /testflight/*
  • release: /release/*
  • standard: *
steps {
    echo "Build type: ${getBuildType()}"
}

Set Job Display Name

Sets name for the running job based on git branch name.

steps {
    setJobDisplayName()
}

Git Branch Name

Get the current branch name.

steps {
    echo "Git branch name: ${git.branchName()}"
}

Git Commit SHA

Get the full git commit sha for current commit.

steps {
    echo "Git commit SHA: ${git.commitSha()}"
}

Git Commit SHA Short

Get the short 7 character git commit sha for current commit.

steps {
    echo "Git short commit SHA: ${git.commitShaShort()}"
}

Git Trailer Value

Get the trailer value for the specified token for the current commit.

steps {
    echo "Git commit message trailer value : ${git.getTrailerValue('some-token')}"
}

GitHub Pull Request Comment

Creates or updates the Jenkins bot issue comment for the pull request.

This can be used to have a comment in the PR that Jenkins updates with current information about the build.

steps {
    github.pullRequestComment(stringWithComment, 'jenkins[bot]')
}

GitHub Owner Repo

Get the <owner/repo> part from the project's git url.

def owner = github.ownerRepo()

GitHub Pull Request

Returns the pull request the current commit belongs to.

def pr = github.pullRequest()

GitHub Issue Comments

Retrieves all comments for an issue/pull request.

def comment = github.issueComments(42)

GitHub Create Issue Comment

Create issue comment.

github.createIssueComment(42, "some comment")

GitHub Update Issue Comment

Update issue comment.

github.updateIssueComment(42, "some updated comment")

GitHub Create Check Run

Create a check run.

Start a check run for a specific build build.

steps {
    script {
        github.createCheckRun('Build iOS', 'queued')
    }
}

GitHub Update Check Run

Update a check run.

steps {
    script {
        github.updateCheckRun('Build iOS', 'in_progress')
    }
}

post {
failure {
    script {
        github.updateCheckRun('Build iOS', '', 'failure')
    }
}

success {
    script {
        github.updateCheckRun('Build iOS', '', 'success')
    }
}

InfluxFB Add Measurement

Add a measurement for writing to InfluxDB. The data type for each field in teh measurement is set with a string.

value Data type
"f" float
"i" integer
"s" string
influxdb.addMeasurement("build",
    [
        platform: platform,
        build_type: buildType
    ],
    [
        code_coverage: ["f", coverage.lineCoverage],
        size: ["i", size],
        duration_unity: ["i", timers.getDuration("Unity.${platform}")],
        duration_xcode: ["i", timers.getDuration("Xcode.${platform}")],
        jenkins_build_number: ["i", currentBuild.number],
        unity_build_number: ["i", unityBuildNumber()],
        commit_sha: ["s", env.GIT_COMMIT_SHA_SHORT]
    ]
)

InfluxFB Write Measurements

Write all added measurements to InfluxDB.

withCredentials([string(credentialsId: 'influxdb', variable: 'INFLUXDB_TOKEN')]) {
    influxdb.write(env.INFLUXDB_HOST, env.INFLUXDB_ORG, env.INFLUXDB_BUCKET, INFLUXDB_TOKEN)
}

Timers Start

Creates and starts a new timer with the specified name.

Multiple timers can be created and started to store the duration for different steps of the pipeline.

timers.start("unity.${platform}")
buildUnity(platform)
timers.stop("unity.${platform}")

Timers Stop

Stops the timer with the specified name.

timers.start("unity.${platform}")
buildUnity(platform)
timers.stop("unity.${platform}")

Timers Get Duration

Gets the duration in ms between start and stop for the timer with the specified name.

echo "Unity iOS Build Duration: ${timers.getDuration('unity.iOS')}"

Utils Get Directory Size

Get the size of the provided directory in bytes.

def size = utils.getDirectorySize('build.app')

Utils Get File Size

Get the size of the provided file in bytes.

def size = utils.getFileSize('build.ipa')

Development

Gradle is used to test the pipeline during development.

Running Tests

The test suite is executed with:

./gradlew test

Then gradle is executed for the first time in a session a daemon is started. Manage the daemon with these commands.

## See running daemons
gradle --status

## Stop running daemons
gradle --stop

macOS

Gradle needs Java runtime, and the version of Gradle used in this project uses Java 17. Install Java 17 with Homebrew and then set JAVA_HOME in the terminal, for the session, in the terminal.

export JAVA_HOME=/usr/local/opt/openjdk@17

Debug Output

During development it can be useful to get output from tests to the console.

@Test
void foo_SomeBar_GetSomeBaz() {
    println(someObjectToDebug)
}

Enable log info level to display println statements in the test output.

./gradlew test --info