<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pedro Pereira Lourenço</title>
    <description>The latest articles on DEV Community by Pedro Pereira Lourenço (@pedro_prlco).</description>
    <link>https://dev.to/pedro_prlco</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F948030%2Fd694e6aa-1522-4251-9a6b-bf79789ade70.jpg</url>
      <title>DEV Community: Pedro Pereira Lourenço</title>
      <link>https://dev.to/pedro_prlco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pedro_prlco"/>
    <language>en</language>
    <item>
      <title>Constantly Delivering my Unity Project builds with Jenkins</title>
      <dc:creator>Pedro Pereira Lourenço</dc:creator>
      <pubDate>Mon, 05 Feb 2024 23:00:42 +0000</pubDate>
      <link>https://dev.to/pedro_prlco/constantly-delivering-my-unity-project-builds-with-jenkins-ehk</link>
      <guid>https://dev.to/pedro_prlco/constantly-delivering-my-unity-project-builds-with-jenkins-ehk</guid>
      <description>&lt;p&gt;Hi!&lt;/p&gt;

&lt;p&gt;In this document, I will explain how I managed to create my own continuous integration and delivery pipeline using &lt;strong&gt;Jenkins&lt;/strong&gt; for my projects in Unity. All the code mentioned here will be available on my &lt;strong&gt;GitHub&lt;/strong&gt; account for free use. There may be various ways to implement what I am presenting here, so feel free to share any thoughts on how I can improve in the future. I would love to hear your feedback.&lt;/p&gt;

&lt;p&gt;One of the many factors that influenced me to learn more about how I can build this Continuous Integration and Continuous Delivery (CICD) process was my old spare notebook that I have at home. To prevent it from collecting dust in my closet, I decided to turn it into a building machine. This way, I could easily connect to it using any of those remote desktop apps and build my applications. Within a matter of minutes, I would have it ready to be tested on any shared devices.&lt;/p&gt;

&lt;p&gt;At this moment, the Pipeline is running in four phases: &lt;strong&gt;checkout&lt;/strong&gt;, &lt;strong&gt;setup&lt;/strong&gt;, &lt;strong&gt;build&lt;/strong&gt;, and &lt;strong&gt;publish&lt;/strong&gt;. I will document each one of these phases and explain how it was implemented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Jenkins
&lt;/h2&gt;

&lt;p&gt;For the Pipelines, I decided to use &lt;strong&gt;Jenkins&lt;/strong&gt;, an open-source automation server. It assists in automating various aspects of software development, including builds, testing, and deployment, thereby simplifying the process of continuous integration and delivery.&lt;/p&gt;

&lt;p&gt;For the sake of simplicity, I have opted to run &lt;strong&gt;Jenkins&lt;/strong&gt; locally on my machine without utilizing any Jenkins image within a separate Docker container. Since it was my first time doing this, I went through various tests and process definitions, involving constant "add" and "remove" actions on files and codes. Additionally, given that my knowledge of Docker is not that advanced, using it would have caused a delay in achieving what I was attempting to do.&lt;/p&gt;

&lt;p&gt;During my research, videos from &lt;a href="https://www.youtube.com/watch?v=6YZvp2GwT0A&amp;amp;t=3393s&amp;amp;ab_channel=DevOpsJourney"&gt;DevOps Journey&lt;/a&gt; and &lt;a href="https://www.youtube.com/@CloudBeesTV"&gt;CloudBeesTV&lt;/a&gt; helped me a lot to understand a little bit more about &lt;strong&gt;Jenkins&lt;/strong&gt;. It didn't take me too long to get comfortable using this awesome tool.&lt;/p&gt;

&lt;p&gt;I have created a repository on &lt;strong&gt;GitLab&lt;/strong&gt; containing all the code needed to run the pipeline. Once &lt;strong&gt;Jenkins&lt;/strong&gt; starts the build, it downloads this repository and runs the &lt;strong&gt;main.groovy&lt;/strong&gt; script. This file is the beginning of the pipeline, running all the steps, from build to uploading to Google Drive. As I said before, this pipeline is split into 4 phases: &lt;strong&gt;checkout&lt;/strong&gt;, &lt;strong&gt;setup&lt;/strong&gt;, &lt;strong&gt;build&lt;/strong&gt;, and &lt;strong&gt;publish&lt;/strong&gt;. These phases run essential commands for generating the build. The diagram of these phases is represented below for a summary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fglu71jgcxm1cg8j97upm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fglu71jgcxm1cg8j97upm.jpg" alt="Image description" width="417" height="441"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'checkout'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Setup'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Publish'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A closer look at the phases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Checkout
&lt;/h3&gt;

&lt;p&gt;First phase, it is required to update the Pipeline to the most recent version. It must stay updated before any builds in case it has changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;The Unity project has its repository. When starting the pipeline, &lt;strong&gt;Jenkins&lt;/strong&gt; updates this repository to the most recent version and then starts the build.&lt;/p&gt;

&lt;p&gt;In this phase, I encountered an issue that started occurring after &lt;strong&gt;git&lt;/strong&gt; had its vulnerability correction, beginning with version &lt;a href="https://github.blog/2022-04-12-git-security-vulnerability-announced/#"&gt;Git v2.35.2&lt;/a&gt;. In short, when &lt;strong&gt;git&lt;/strong&gt; attempts to run a command inside another repository, it begins respecting any configurations in that &lt;strong&gt;git&lt;/strong&gt; directory. This could lead to a security problem, especially in cases where sensitive configurations are defined.&lt;/p&gt;

&lt;p&gt;As my pipeline already runs inside its repository, when attempting to execute &lt;code&gt;git checkout &amp;lt;branch&amp;gt;&lt;/code&gt; from within Unity's repository, it triggers the fatal error &lt;code&gt;unsafe repository&lt;/code&gt;. The error itself suggests a solution that would likely resolve this issue, which is to add Unity's repository to a list of trusted folders. This should allow me to run the command again. However, the fatal error persists.&lt;/p&gt;

&lt;p&gt;After numerous attempts, I successfully resolved it by creating a &lt;a href="https://github.com/lourenco-pedro/git_updater"&gt;simple C# console&lt;/a&gt; directly within Unity's repository. This program then executes the &lt;strong&gt;git&lt;/strong&gt; command, specifying the target branch to checkout. With this workaround, I could update the project, leaving it ready for building in the next phase.&lt;/p&gt;

&lt;p&gt;Here is an example of how this program runs the Git command, wrapping every Git functionality that might be necessary for this phase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;branchName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

            &lt;span class="n"&gt;Git&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Git&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;branchName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FetchAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Checkout&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IndexOutOfRangeException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"**ERROR:** There are no arguments provided specifying the desired branch to checkout!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the end, the &lt;strong&gt;Jenkins&lt;/strong&gt; commands for this phase ended up like this, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Setup'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cmd'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating unity project by using git solution"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"${env.UNITY_PROJECT_PATH}/git_solution/"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"dotnet run ${env.GIT_BRANCH_TO_CHECKOUT}"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build
&lt;/h3&gt;

&lt;p&gt;As the name already suggests, this phase generates the Unity project's build. Unity offers us a way to interact with its editor by providing arguments from the command line. This simplifies the process when working to achieve continuous integration for your game. All the information about how you can interact with the Unity Editor using only command lines is available in &lt;a href="https://docs.unity3d.com/Manual/EditorCommandLineArguments.html"&gt;Unity's official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Building a Unity project via the command line looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"C:&lt;/span&gt;&lt;span class="se"&gt;\P&lt;/span&gt;&lt;span class="s2"&gt;rogram Files&lt;/span&gt;&lt;span class="se"&gt;\U&lt;/span&gt;&lt;span class="s2"&gt;nity&lt;/span&gt;&lt;span class="se"&gt;\H&lt;/span&gt;&lt;span class="s2"&gt;ub&lt;/span&gt;&lt;span class="se"&gt;\E&lt;/span&gt;&lt;span class="s2"&gt;ditor&lt;/span&gt;&lt;span class="se"&gt;\&amp;lt;&lt;/span&gt;&lt;span class="s2"&gt;version&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\E&lt;/span&gt;&lt;span class="s2"&gt;ditor&lt;/span&gt;&lt;span class="se"&gt;\U&lt;/span&gt;&lt;span class="s2"&gt;nity.exe"&lt;/span&gt; &lt;span class="nt"&gt;-quit&lt;/span&gt; &lt;span class="nt"&gt;-batchmode&lt;/span&gt; &lt;span class="nt"&gt;-projectPath&lt;/span&gt; &lt;span class="s2"&gt;"C:&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;&lt;span class="s2"&gt;ath&lt;/span&gt;&lt;span class="se"&gt;\f&lt;/span&gt;&lt;span class="s2"&gt;or&lt;/span&gt;&lt;span class="se"&gt;\U&lt;/span&gt;&lt;span class="s2"&gt;nity&lt;/span&gt;&lt;span class="se"&gt;\P&lt;/span&gt;&lt;span class="s2"&gt;roject"&lt;/span&gt; &lt;span class="nt"&gt;-executeMethod&lt;/span&gt; &amp;lt;Namespace.Class.MethodName&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Taking a look at the shell command above, the argument &lt;code&gt;-batchmode&lt;/code&gt; implies that Unity will run in batch mode, and every interaction will be through command lines, ignoring any human action in the Editor. This also suppresses any pop-ups that might appear during the build. Following the next command, I informed the &lt;code&gt;projectPath&lt;/code&gt; to open. In the end, when the project finishes loading, it executes the method passed in the &lt;code&gt;executeMethod&lt;/code&gt; argument.&lt;/p&gt;

&lt;p&gt;Before &lt;strong&gt;Jenkins&lt;/strong&gt; starts the build, I would like to have control over more aspects of how the build will be generated. It would be great to increase the build version, specify whether it will be an &lt;strong&gt;.apk&lt;/strong&gt; or &lt;strong&gt;.aab&lt;/strong&gt;, indicate if it needs to split application binaries and configure other settings as my project grows. To encapsulate all these settings before the build, I have created &lt;a href="https://github.com/lourenco-pedro/UnityBuilder"&gt;Unity Builder&lt;/a&gt;, a package that streamlines the Unity build generation process by executing all necessary steps beforehand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lourenco-pedro/UnityBuilder"&gt;Unity Builder&lt;/a&gt; has the method &lt;code&gt;UnityBuilder.BuildCmd.Build&lt;/code&gt; that is passed in the &lt;code&gt;-executeMethod&lt;/code&gt; argument in the shell command above. When running this method, Unity loads the &lt;code&gt;ScriptableObject&lt;/code&gt; &lt;code&gt;BuildCmd&lt;/code&gt;, which holds every configuration defined by the user on how it needs to be generated. It also increases the version as programmed by the user.&lt;/p&gt;

&lt;p&gt;Every build from &lt;a href="https://github.com/lourenco-pedro/UnityBuilder"&gt;Unity Builder&lt;/a&gt; is saved in the path specified in the environment variable &lt;code&gt;UNITY_BUILDER_ROOT&lt;/code&gt;. It is necessary to have this environment variable defined; otherwise, the build process will end with the result as &lt;code&gt;INVALID_ENVIRONMENTS&lt;/code&gt;. &lt;code&gt;UNITY_BUILDER_ROOT&lt;/code&gt; also has the &lt;strong&gt;.json&lt;/strong&gt; file &lt;code&gt;versionSettings&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;versionSettings&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"isRelease"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Path from the last generated build"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;isRelease&lt;/code&gt; parameter informs whether &lt;code&gt;UnityBuilder&lt;/code&gt; needs to increase the main version number or not (&lt;code&gt;v.MainVersionNumber.BuildNumber&lt;/code&gt;, &lt;code&gt;v.1.1&lt;/code&gt;). The &lt;code&gt;path&lt;/code&gt; parameter indicates where the last build by &lt;a href="https://github.com/lourenco-pedro/UnityBuilder"&gt;Unity Builder&lt;/a&gt; was stored. This field will later be used by &lt;strong&gt;Jenkins&lt;/strong&gt; when publishing to Google Drive.&lt;/p&gt;

&lt;p&gt;At the end, the &lt;strong&gt;Jenkins&lt;/strong&gt; commands for this phase ended up like this, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Building Unity project..."&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cmd'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;unity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'unity'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Publish
&lt;/h3&gt;

&lt;p&gt;In the final phase of the pipeline, at this moment, &lt;strong&gt;Jenkins&lt;/strong&gt; sends the new fresh build to Google Drive. While I plan to use this phase in the future for uploading builds to stores, currently, for internal tests and since my project is in its early stages, I have opted to use only Google Drive. This way, it will be easier to share my builds with other people for testing purposes.&lt;/p&gt;

&lt;p&gt;In this phase, I have created two more C# programs that make the process easy when uploading to Google Drive: the &lt;code&gt;drive_uploader&lt;/code&gt; and &lt;code&gt;upload_build_to_drive&lt;/code&gt;. The &lt;code&gt;drive_uploader&lt;/code&gt; is a service that implements the Google Drive API. It works by simply running the following command: &lt;code&gt;dotnet run -file &amp;lt;path&amp;gt; -mime &amp;lt;type&amp;gt;&lt;/code&gt;, where &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; is the path of the file that will be sent to Drive, and &lt;code&gt;&amp;lt;type&amp;gt;&lt;/code&gt; is the type of file that will be sent. &lt;a href="https://github.com/lourenco-pedro/driver_uploader"&gt;This service is also available on my GitHub for free use&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, the &lt;code&gt;upload_build_to_drive&lt;/code&gt; uses this service by loading the &lt;code&gt;versionSettings.json&lt;/code&gt; mentioned above and passing the path of the recently generated build in the &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; argument. As for the &lt;code&gt;-mime&lt;/code&gt; parameter, there are two values that can be passed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;.aab&lt;/strong&gt; files, &lt;code&gt;&amp;lt;type&amp;gt;&lt;/code&gt; will be &lt;code&gt;application/x-authorware-bin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;.apk&lt;/strong&gt; files, &lt;code&gt;&amp;lt;type&amp;gt;&lt;/code&gt; will be &lt;code&gt;application/vnd.android.package-archive&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end, the &lt;strong&gt;Jenkins&lt;/strong&gt; commands for this phase ended up like this, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Publish'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cmd'&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Publishing app to drive"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DRIVE_UPLOADER_PATH&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Do&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"dotnet run"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  In conclusion
&lt;/h2&gt;

&lt;p&gt;Several improvements can be applied during the presented process. But as a first attempt, I am happy with the result I managed to achieve. Now, for any change I make in my project that requires mobile testing, I can obtain it with just one click. This document was not written to teach how to create your own Continuous Integration and Continuous Delivery Pipeline, but rather to document the processes I performed to achieve this result. Feel free to provide any constructive feedback for improvements in what has been presented.&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

&lt;p&gt;You can also find me in:&lt;br&gt;
&lt;a href="https://www.linkedin.com/in/pedro-pereira-7694b7166/"&gt;LinkedIn&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/lourenco-pedro"&gt;GitHub&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/channel/UC7YMkk46fdSzIUVpgcI9mWw"&gt;Youtube&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=-Wg6S9pZUeQ&amp;amp;t=459s"&gt;Lecture about OOP Applied in Games&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jenkins</category>
      <category>unity3d</category>
      <category>cicd</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
