CI/CD setup with Unity, Plastic SCM, TeamCity : The Implementation Part-2

Soumya Ranjan Sahoo
5 min readJun 5, 2022

Note : This article is about how to setup the CI/CD setup with unity, plastic scm, teamcity and the important coding part for it.

haha, making builds can become exhausting if you don’t have the correct process. (src : Unsplash)

Intro

Last article I explained the use case of CI/CD for our game. Now let’s get down to the implementation. I will simply explain and share you some of the resources that I followed to set it up. Then the respective code for some build configs.

(Execution flow for a build triggered)

Useful Resources

I won’t go fully in detail as this setup can vary for different teams as per their requirements. I am sharing some specific code and link references hoping it helps.

Following sources were helpful in setting up the Teamcity with Unity and Plastic for me.
1. https://www.youtube.com/watch?v=Sh_BRCw7prc&t=384s [CI for Unity Games Using TeamCity]

2.https://www.jetbrains.com/help/teamcity/continuous-integration-with-teamcity.html#Basic+TeamCity+concepts [This doc has everything that you want to know and guide yourself through the Teamcity CI/CD software]

3. https://www.plasticscm.com/documentation/technical-articles/how-to-integrate-plastic-scm-with-teamcity-ci [Setting up Plastic SCM in Teamcity]

Once all of these base software are setup then you will be needing to create build configs.
For the Unity Build Script which will be called from Teamcity you can refer this link : https://github.com/Nordeus/UnityBuildPipeline

Note: Please go through these links as I don’t explain the exact step by step setup here.

CI/CD build configs flow

I have three build configs
1. Quest Build Config : makes the executable build for Oculus Quest.
2. Upload Build Config : This is triggered once the Quest Build Config Finishes.
- This will zip the out put apk file and will upload to the share point.
- Once the file is uploaded then it sends a notification on teams group and a mail to the respective users.
- Also if build fails you get a notification.
3. Revert VCS Build Config : This config will revert the local VCS changes/checkouts after the build is completed to make sure no project files are checked out by the build PC after the build is complete.

Code : Build Script (C#)

The following script is a slightly modified version of this one : https://github.com/Nordeus/UnityBuildPipeline.
The below script generates a signed apk with the timestamp in its name so its easier to track the builds in history.

public static void BuildAndroid(){
string buildVersion = "1", buildVersionCode = "1", buildNameForQuestBase = "AutomatedBuildQuest_QA";
// Create a time stamp for adding to build name
string timeStamp = "_" + System.DateTime.Now.Month.ToString() + "M" + System.DateTime.Now.Day.ToString() + "D" + System.DateTime.Now.Hour.ToString() + "H" + System.DateTime.Now.Minute.ToString() + "m";
List<string> scenePaths = new List<string>();foreach (EditorBuildSettingsScene scenePath in EditorBuildSettings.scenes)
{
if (scenePath.enabled)
scenePaths.Add(scenePath.path);
}
string[] allBuildScenes = scenePaths.ToArray();
buildVersion = PlayerSettings.bundleVersion;
buildVersionCode = PlayerSettings.Android.bundleVersionCode.ToString();
BuildPlayerOptions buildPlayerOptionsAndroid = new BuildPlayerOptions();
buildPlayerOptionsAndroid.scenes = allBuildScenes;
string fullBuildNameForQuest = buildNameForQuestBase + "_" + buildVersion + "_" + buildVersionCode + timeStamp;buildPlayerOptionsAndroid.locationPathName = "BuildsOutput/Quest/Build/" + fullBuildNameForQuest + ".apk";buildPlayerOptionsAndroid.target = BuildTarget.Android;
buildPlayerOptionsAndroid.options = BuildOptions.None;
PlayerSettings.Android.useCustomKeystore = true;
PlayerSettings.Android.keystoreName = "Assets/user.keystore"; // provide path to the keystore
PlayerSettings.Android.keystorePass = "--------";
PlayerSettings.Android.keyaliasName = "---------";
PlayerSettings.Android.keyaliasPass = "---------";
// Check and create the Directories if doesn't exist
CreateBuildPaths();
// Write the build name to file
WriteBuildNameToFile(BuildTarget.Android, fullBuildNameForQuest);
Debug.Log("BuildScript : Starting android build.");// Start the build
BuildReport buildReport =
BuildPipeline.BuildPlayer(buildPlayerOptionsAndroid);
BuildSummary buildSummary = buildReport.summary;
}

Code: Compressing file with Powershell

Creates two zip copies of the output build file. One as a common back up of last build and the other one with date-time stamp to upload to share point.
Once upload complete, it will delete the second zip file.

# Retrive the build name from the text file and give the same to compressed zip file name
$destinationQuestBuildNameFile = "D:\Workspace\Devops\TeamCity\buildAgent\work\9749330ae955974\BuildsOutput\Quest\BuildNameQuest.txt"
$QuestCompressedFileName = "QuestBuildCompressed.zip"
if((Test-Path -path $destinationQuestBuildNameFile))
{
$QuestCompressedFileName = Get-Content $destinationQuestBuildNameFile
}
# Make a zip folder of the target Quest build
$destinationQuestBuildFolder = "D:\Workspace\Devops\TeamCity\buildAgent\work\9749330ae955974\BuildsOutput\Quest\Build"
$destinationQuestBuildZipFile = "D:\Workspace\Devops\TeamCity\buildAgent\work\9749330ae955974\BuildsOutput\Quest\"+$QuestCompressedFileName+".zip"
$destinationQuestBuildBackupZipFile = "D:\Workspace\Devops\TeamCity\buildAgent\work\9749330ae955974\BuildsOutput\Quest\QuestBuildCompressed_Backup.zip"
Compress-Archive -Path $destinationQuestBuildFolder -DestinationPath $destinationQuestBuildZipFile -Force
Start-Sleep -Seconds 10
Copy-Item $destinationQuestBuildZipFile $destinationQuestBuildBackupZipFile
Start-Sleep -Seconds 10
write-host "Build Compressed locally."

Code : Uploading large files to Sharepoint

Sharepoint has a default limit of 250MB file size upload limit. Below powershell script will work for uploading higher file sizes > 250MB.

#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

#Set parameter values
$SiteURL="https://xyz.sharepoint.com/sites/GameBuilds"
$SourceFilePath=$destinationQuestBuildZipFile
$TargetFolderRelativeURL ="/sites/Builds/Shared Documents/General/Builds/Game/AutomatedBuild/QA"
#Setup Credentials to connect
$AdminName =" admin mail id"
$AdminPassword ="pswd"
#Setup Credentials to connect
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($AdminName,(ConvertTo-SecureString $AdminPassword -AsPlainText -Force))
Try {
#Setup the context
$Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Ctx.Credentials = $Credentials

#Get the file from disk
$FileStream = ([System.IO.FileInfo] (Get-Item $SourceFilePath)).OpenRead()

#Calculate Target URL
$SourceFileName = Split-path $SourceFilePath -leaf
$TargetFileURL = $TargetFolderRelativeURL+"/"+$SourceFileName

Write-Output "Upload job started $(Get-Date)"
#Upload Large File to SharePoint Online
$Ctx.RequestTimeout = [System.Threading.Timeout]::Infinite
[Microsoft.SharePoint.Client.File]::SaveBinaryDirect($Ctx, $TargetFileURL, $FileStream,$True)
Write-Output "Upload job finished $(Get-Date)"
#Close file stream
$FileStream.Close()
write-host "File uploaded Successfully!" -f Green
}
Catch {
write-host "Error Uploading File: $($_.Exception.Message)" -foregroundcolor Red
}
#Read more: https://www.sharepointdiary.com/2020/05/upload-large-files-to-sharepoint-online-using-powershell.html#ixzz7QExsdVDA

Code : Send mail notification (Powershell)

This will send an email notification to the target mail ids.

#Send mail to user group
$SmtpServer = 'smtp.office365.com'
$SmtpUser = 'admin mail id'
$smtpPassword = 'pswd'
$MailtTo = 'target user group mail id'
$MailFrom = 'admin mail id'
$MailSubject = "Quest Build Ready $SmtpServer"
$BodyOfMessage = "link : https://xyz.sharepoint.com/sites/ProjectTeam/Shared%20Documents/General/Game%20St/Builds/Game/AutomatedBuild/QA"
$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $SmtpUser, $($smtpPassword | ConvertTo-SecureString -AsPlainText -Force)
Send-MailMessage -To "$MailtTo" -from "$MailFrom" -Subject $MailSubject -Body $BodyOfMessage -SmtpServer $SmtpServer -UseSsl -Credential $Credentials
write-host "Sent notifications to users"

Code : Send Teams notification

This will send a teams channel notification using webhook. So you are immediately notified from teams itself.
It will also delete the duplicated zip file that was created for uploading.

# Send notifications to teams channel
$JSONBody = [PSCustomObject][Ordered]@{
"@type" = "MessageCard"
"@context" = "http://schema.org/extensions"
"summary" = "Incoming Alert Test Message!"
"themeColor" = '0078D7'
"title" = "Quest Build is complete"
"text" = "Get latest build here : https://xyz.sharepoint.com/sites/ProjectTeam/Shared%20Documents/General/Game%20St/Builds/Game/AutomatedBuild/QA "
}
$TeamMessageBody = ConvertTo-Json $JSONBody -Depth 100
$parameters = @{
"URI" = 'https://xyz.webhook.office.com/webhookb2/...'
"Method" = 'POST'
"Body" = $TeamMessageBody
"ContentType" = 'application/json'
}
Invoke-RestMethod @parameters | Out-Null
# Clear all the files in the build folder once zip uploaded [Keep the zip]
Get-ChildItem -Path $destinationQuestBuildFolder -Include * | remove-Item -recurse
Write-Output "Waiting for 10sec $(Get-Date)"
# Wait for 3mins before deleting the zip so it gets uploaded first
Start-Sleep -Seconds 10
Write-Output "Deleting the zip file $(Get-Date)"
# Delete the zip file which will was uploaded to cloud
if((Test-Path -path $destinationQuestBuildZipFile))
{
Get-ChildItem -Path $destinationQuestBuildZipFile -Include *.* -File -Recurse | foreach {$_.Delete()}
}

Code: Revert the VCS changes

Once the build is done there will be some projects files that will be locally checked out by the vcs. This script undo the changes for Plastic SCM in the local workspace path. You can replace this as per your own vcs.

cd d:
cd D:\Workspace\Devops\TeamCity\buildAgent\work\9749330ae955978
cm unco --all

That’s all. Wish you have a smooth CI/CD setup.

Peace n Love. 🙂

--

--