Azure DevOps Unity3D pipeline

August 2020. This is an article to accompany my video about setting up an Azure DevOps pipeline for a local build agent with Unity3D.

For those who want the files: https://gamefeelingsblog.blob.core.windows.net/supportfiles/helper-files-for-azure-devops-unity-build-2.zip

And here the links to a few other resources:

Why Azure DevOps

I did a video on a Jenkins setup this year. In that video I show how to setup a local build system for Unity3D with Jenkins.

Azure DevOps is a CI/CD tool. That means it facilitates continues integration and continues deployment. Why you want to use CI/CD is a story on its own and has to do with facilitating iterative development, see for instance this article from me about this subject.

Doing CI/CD was already kind-of a practice in game dev. It was often called a pipeline and revolved around getting newly build stuff integrated in the existing code base. So the current move of automating and streamlining more stuff into pipelines is only an evolution of this old practice. You only have to pay attention on what to automate and how stringent you are, but other that that it is a worthwhile exercise. See a good YouTube about this: https://www.youtube.com/watch?v=t9HRzE7_2Xc.

So why Azure DevOps? Well, I’m a Microsoft developer from when I grew up and have used their tools for over decades now. I do find that over time I like their approach to empowering the developer with good working tools, I tried a few other tools but always got back to them having the better maintained and better thought-out philosophy.

I tried Jenkins but found it to be outdated and non intuitive. So yeah, I do have a few (payed) Microsoft accounts already so for me Azure DevOps is the better choice.

Update: a major reason for me to use Azure DevOps is because it integrates different aspects of ‘good’ CI/CD practices a lot better. In Jenkins you need a lot of plugins to make stuff work, and ‘pipeline as code’ is not integrated as good as in Azure DevOps.

Helper files

If you follow the tutorial you see me using a batch file, a setup, and a specific pipeline.

You can download the content in a zip over here, or copy it from the source code paragraphs below.

(Replace the game name, organization name, and project name specific folders with your names. In all files. See the zip for the folder structure as I used them with these scripts for reference.)

The pipeline yaml file:

# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml

trigger:
- master

pool:
  name: Default
  demands: 
    Unity_2019.3.2f1

steps:
#cleanup for unity build
- task: DeleteFiles@1
  inputs:
    SourceFolder: '$(Build.ArtifactStagingDirectory)'
    Contents: '**/*'

#get the previously secured library (if any) for performance reasons
- task: DeleteFiles@1
  inputs:
    SourceFolder: '$(Build.Repository.LocalPath)\Unity Source\Manage the Universe\Library'
    Contents: '**/*'
- task: CmdLine@2
  inputs:
    script: 'if EXIST "$(Build.BinariesDirectory)\Library\" move "$(Build.BinariesDirectory)\Library" "$(Build.Repository.LocalPath)\Unity Source\Manage the Universe\Library"'

#unity PC build
- task: UnityBuildTask@3
  inputs:
    buildTarget: 'Win64'
    unityProjectPath: 'Unity Source\Manage the Universe'
    outputPath: '$(Build.BinariesDirectory)\Build'
    outputFileName: 'ManageTheUniverse'

#create the installer
- task: BatchScript@1
  inputs:
    filename: 'Installer/createsetup.bat'
    arguments: '"$(Build.BinariesDirectory)\Build\*" "$(Build.ArtifactStagingDirectory)\SetupCreation" "0.1.$(Build.BuildNumber)"'
    workingFolder: '$(Build.Repository.LocalPath)\Installer'

#upload artifact
- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)\SetupCreation'
    ArtifactName: 'drop'
    publishLocation: 'Container'

#secure the library for performance reasons
- task: DeleteFiles@1
  inputs:
    SourceFolder: '$(Build.BinariesDirectory)\Library'
    Contents: '**/*'
- task: CmdLine@2
  inputs:
    script: 'move "$(Build.Repository.LocalPath)\Unity Source\Manage the Universe\Library" "$(Build.BinariesDirectory)\Library"'

The setup batch file:

set arg1=%~1
set arg2=%~2
set arg3=%~3

"Inno Setup 6\ISCC.exe" "/DBuildSource=%arg1%" "/DBuildTarget=%arg2%" "/DMyAppVersion=%arg3%" setup.iss

The Inno setup 6 file:

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "ManageTheUniverse"
#define MyAppPublisher "GameFeelings"
#define MyAppURL "https://gamefeelings.com/"
#define MyAppExeName "managetheuniverse.exe"

[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{A3C19EB9-57D2-4DCD-870D-9A8A0CCCBFB2}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
; Remove the following line to run in administrative install mode (install for all users.)
PrivilegesRequired=lowest
OutputDir={#BuildTarget}
OutputBaseFilename=SetupPc
Compression=lzma
SolidCompression=yes
WizardStyle=modern

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "{#BuildSource}"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

Admin Access

You need to have a high access level in your Azure DevOps organizations to pull this off. So in an existing organization with a DevOps account already in use you probably need to work with the admins to get everything going.

However if you have your own (free) account you by default have the admin role.

Azure DevOps plan limitations

Depending on your DevOps account plan you have limitations to the amount of running agents. And you have limited build minutes a month.

If you are on a free version you probably are limited to 1 local build agent, and have 1800 free build minutes a month (and for more you pay).

Even with a local build agent a few things still count for build minutes like uploading your artifacts or coordinating stuff.

Next level

Update: In April 2021 I released a blog with an evolution of the pipeline featured in the video. Better yaml practices, different builds, although not accompanied by much explanation.

Update: As of June 2021 I am working on an improved tutorial that will show better CI/CD practices in general and how to release automatically to Steam.

Published by Erik_de_Roos

Erik de Roos is a Freelance software developer.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: