{"id":2861,"date":"2019-09-09T10:00:09","date_gmt":"2019-09-08T22:00:09","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2861"},"modified":"2019-09-22T09:59:44","modified_gmt":"2019-09-21T21:59:44","slug":"azure-devops-building-some-code","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2861\/","title":{"rendered":"Azure DevOps &#8211; Building Some Code"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span>\n<p>After <a href=\"https:\/\/www.deltics.co.nz\/blog\/posts\/2659\">the previous post<\/a> we should have ourselves a build agent with everything we need to perform command line builds of Delphi code. But let&#8217;s make sure!<\/p>\n\n\n\n<!--more-->\n\n\n\n<p class=\"has-drop-cap\">The first thing we need is some code, so we&#8217;ll need a git repository in Azure DevOps.  Before we can get to that though, we need a <strong>Project<\/strong>.  In Azure DevOps, a project is a container for any number of repositories you may wish but also backlogs for managing work items relating to those repositories and much else besides.<\/p>\n\n\n\n<p>Projects may be <strong>private<\/strong> or <strong>public<\/strong> and which you choose depends on your collaboration model.  Private repositories can still be used collaboratively but people need to be invited to join those projects.<\/p>\n\n\n\n<p>But it is not the aim of these posts to get into the details of Azure DevOps (maybe another time), so for our purposes we&#8217;ll create a private <strong>Hello Delphi<\/strong> project:<\/p>\n\n\n\n<p><img decoding=\"async\" loading=\"lazy\" width=\"640\" height=\"296\" class=\"wp-image-2863\" style=\"width: 1024px;\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.11.56.png?resize=640%2C296&#038;ssl=1\" alt=\"\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.11.56.png?w=1361&amp;ssl=1 1361w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.11.56.png?resize=300%2C139&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.11.56.png?resize=768%2C356&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.11.56.png?resize=1024%2C474&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.11.56.png?resize=380%2C176&amp;ssl=1 380w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.11.56.png?w=1280&amp;ssl=1 1280w\" sizes=\"(max-width: 640px) 100vw, 640px\" data-recalc-dims=\"1\" \/><\/p>\n\n\n\n<p>Once created (it takes a few seconds) we can navigate to the project and its repositories.  Initially there is a single, default repository (named for the project) but it contains no code at this point.  The quickest and simplest way to get something into that repo is to add a <strong>README.MD<\/strong> and <strong>.gitignore<\/strong> directly from the Azure DevOps UI:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" loading=\"lazy\" width=\"640\" height=\"354\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.18.17.png?resize=640%2C354&#038;ssl=1\" alt=\"\" class=\"wp-image-2864\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.18.17.png?resize=1024%2C566&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.18.17.png?resize=300%2C166&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.18.17.png?resize=768%2C425&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.18.17.png?resize=380%2C210&amp;ssl=1 380w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.18.17.png?w=1362&amp;ssl=1 1362w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.18.17.png?w=1280&amp;ssl=1 1280w\" sizes=\"(max-width: 640px) 100vw, 640px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>Here we <span style=\"text-decoration: underline;\">do<\/span> find some awareness of Delphi as a default <strong>.gitignore<\/strong> file is available!  \\o\/<\/p>\n\n\n\n<p class=\"has-drop-cap\">Once we&#8217;ve initialised the repo we are presented with the contents of that repo (the <strong>README.MD<\/strong> and <strong>.gitignore<\/strong> we just created) and can now add additional files.  Adding folders is a bit trickier (even though it is offered as an option) because git repos do not track empty folders; to create a folder you actually need to create a file and specify a new path involving the new folder.  But that\u2019s not a problem right now as our next step is to create a simple Delphi project (i.e. dpr) to exercise our build agent with.<\/p>\n\n\n\n<p>We could of course clone the repo to a machine and work there, pushing our changes to the Azure DevOps repo as we go, but everything we need to do for this exercise we can do directly from with Azure DevOps.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" loading=\"lazy\" width=\"640\" height=\"206\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.26.19.png?resize=640%2C206&#038;ssl=1\" alt=\"\" class=\"wp-image-2865\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.26.19.png?resize=1024%2C330&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.26.19.png?resize=300%2C97&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.26.19.png?resize=768%2C247&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.26.19.png?resize=380%2C122&amp;ssl=1 380w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.26.19.png?w=1364&amp;ssl=1 1364w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-09.26.19.png?w=1280&amp;ssl=1 1280w\" sizes=\"(max-width: 640px) 100vw, 640px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>However you choose to do it, we just need two files:<\/p>\n\n\n\n<ol><li><strong>HelloDelphi.dpr<\/strong> &#8211; a simple Delphi project which we can compile on our build agent<\/li><li><strong>build.yml <\/strong>&#8211; a YAML build pipeline that describes the build job. (This can actually be named whatever we like)<\/li><\/ol>\n\n\n\n<p>After adding each file you are presented with a file editor to create some initial content for the file before it is committed to the repo.<\/p>\n\n\n\n<p>First, <strong>HelloDelphi.dpr<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-pascal\">\/\/ File: Hello Delphi \/ HelloDelphi.dpr\n\n{$APPTYPE CONSOLE}\n\nprogram HelloDelphi;\nbegin\n  WriteLn(&#039;Hello Delphi!&#039;);\nend.<\/code><\/pre>\n\n\n\n<p>And then <strong>build.yml<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-yaml\"># File: build.yml\n\ntrigger:\n- master\n\npool:\n  name: The Den\n  demands: Delphi.7\n\nsteps:\n- powershell: |\n    c:\\dcc\\7\\bin\\dcc32.exe HelloDelphi.dpr -Uc:\\dcc\\7\\lib -CC\n    if (Test-Path -path HelloDelphi.exe) {\n        .\\HelloDelphi.exe\n    }\n  displayName: Build and run tests (Delphi 7)<\/code><\/pre>\n\n\n\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/devops\/pipelines\/yaml-schema?view=azure-devops&amp;tabs=schema\">Full documentation of the YAML build format<\/a> is beyond the scope of these posts but is available <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/devops\/pipelines\/yaml-schema?view=azure-devops&amp;tabs=schema\">from Microsoft<\/a>.<\/p>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\"><strong>NOTE:<\/strong> <em>YAML can be an infuriating format.  It relies on whitespace and indentation to convey structure and meaning and errors can be very difficult to spot unless using a YAML aware editor, such as the online one provided by Azure DevOps itself.  Even then, an error highlighted in your YAML might be caused by a whitespace problem elsewhere in the file!<\/em><\/p>\n\n\n\n<p>For my build step I have chosen a <strong>powershell<\/strong> script but I could equally have used a simple <strong>cmd.exe<\/strong> based script by using <code>- script<\/code> in place of <code>- powershell<\/code> .&nbsp;<\/p>\n\n\n\n<p>In addition to the build script itself, there is some additional configuration that tells Azure DevOps how to work with this build:<\/p>\n\n\n\n<ul><li>The <code>- trigger<\/code> identifies that the build is configured to be triggered by changes on the <strong>master<\/strong> branch.<\/li><\/ul>\n\n\n\n<ul><li>The <code>- pool<\/code> identifies that this build is to run in the agent pool called <strong>The Den<\/strong> and requires an agent that has the capability <strong><code>Delphi.7<\/code><\/strong>.<\/li><\/ul>\n\n\n\n<p>Remember, the user-defined capabilities I added to my agent identify which specific Delphi versions are available on the machine and the location in which they are to be found.  You will note however that in this script, the location of the Delphi 7 compiler is hard-coded.<\/p>\n\n\n\n<p>Unfortunately I haven&#8217;t yet found a way to obtain the values of user-defined capabilities in a pipeline that doesn&#8217;t involve creating a custom task which is <em>way<\/em> beyond the scope of these posts.<\/p>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\">There is <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=fernfrost.agent-capabilities\">a plug-in in the Visual Studio Marketplace<\/a> which purports to make these values available via environment variables but I haven&#8217;t yet succeeded in getting this to work (it just crashes my pipeline).<\/p>\n\n\n\n<p>I am using Delphi 7 for this initial test simply because it requires the least configuration of all the Delphi versions to get a simple build up and running but you should be able to adapt this to whatever version you need.  If you haven&#8217;t previously configured command line builds for later versions, be aware that things became significantly more complicated with support for platforms other than Windows, but it&#8217;s still not that difficult.<\/p>\n\n\n\n<p>In a later post we&#8217;ll see a much more comprehensive approach.<\/p>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\">I anticipate that some will suggest I use MS Build but I haven\u2019t yet investigated whether MS Build can be successfully used with side-by-side Delphi installs without full IDE installation.  In any event, for this exercise this would be an unnecessary additional complication since at the very least it relies on IDE created configuration files.<\/p>\n\n\n\n<p>Clearly a great deal more work is needed to turn this into something suitable for production use with real-world Delphi applications, but remember that the aim here is to test the basic ability of the agent to build with Delphi.  Keeping things simple will help us focus on that without getting bogged down in the details (which will come later).<\/p>\n\n\n\n<p class=\"has-drop-cap\">At this point<strong> build.yml<\/strong> is just a file in our repo and isn&#8217;t doing anything for us.  To turn it into something useful we need to create a pipeline and associate this file with it.<\/p>\n\n\n\n<p>To do this, we go to the <strong>Pipelines<\/strong> area of our <strong>Hello Delphi<\/strong> project.  Since we don\u2019t have any currently we are prompted to create our first one and choose to create a pipeline whose code is stored in <strong>Azure Repos Git<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" loading=\"lazy\" width=\"640\" height=\"368\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-10.52.41.png?resize=640%2C368&#038;ssl=1\" alt=\"\" class=\"wp-image-2872\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-10.52.41.png?w=841&amp;ssl=1 841w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-10.52.41.png?resize=300%2C172&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-10.52.41.png?resize=768%2C441&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-10.52.41.png?resize=380%2C218&amp;ssl=1 380w\" sizes=\"(max-width: 640px) 100vw, 640px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>Following the prompts, we select the repo (there&#8217;s only the one we were just working with) and can then choose either to start with a <strong>Starter Pipeline<\/strong> (i.e. a new file with some <em>very<\/em> basic content which won\u2019t be of any use to us) or identify an existing YAML file.  This latter option is what we need since we just created the file we need (<strong>build.yml<\/strong>).<\/p>\n\n\n\n<p>Once we have chosen the file, we are dropped into the pipeline editor (a basic text editor with some YAML and pipeline syntax smarts) where we can make some final tweaks and correct any of those pesky whitespace errors I mentioned.  But we should be good to go and can just run the pipeline.<\/p>\n\n\n\n<p>Once it completes (hopefully) successfully, you can then drill into the job details and should see something like this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" loading=\"lazy\" width=\"640\" height=\"515\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-11.13.16.png?resize=640%2C515&#038;ssl=1\" alt=\"\" class=\"wp-image-2873\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-11.13.16.png?w=964&amp;ssl=1 964w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-11.13.16.png?resize=300%2C241&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-11.13.16.png?resize=768%2C618&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-11.13.16.png?resize=323%2C260&amp;ssl=1 323w\" sizes=\"(max-width: 640px) 100vw, 640px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>Each of the steps in the job can be expanded to see more detail.  Most of these steps are boiler-plate operations performed by Azure DevOps to send work to the agent, including checking out the repo to provide the necessary files for the agent to work on.<\/p>\n\n\n\n<p>The step we&#8217;re most interested in is the middle one: <strong>Build and run tests (Delphi 7)<\/strong>.  This was the <code>displayName<\/code> we specified for our build step so expanding this we should see the output from the <strong>powershell<\/strong> step in our <strong>build.yml<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" loading=\"lazy\" width=\"640\" height=\"459\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-11.16.34.png?resize=640%2C459&#038;ssl=1\" alt=\"\" class=\"wp-image-2874\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-11.16.34.png?w=676&amp;ssl=1 676w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-11.16.34.png?resize=300%2C215&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-07-at-11.16.34.png?resize=362%2C260&amp;ssl=1 362w\" sizes=\"(max-width: 640px) 100vw, 640px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>And there we have it.<\/p>\n\n\n\n<p>We can see that our simple script to run the compiler was indeed successful and that following compilation we identified the resulting <strong>exe<\/strong> file and ran it to obtain the <code>Hello Delphi!<\/code> output to the console.<\/p>\n\n\n\n<p class=\"has-background has-light-green-cyan-background-color\">I just said that you can drill into this detail once the job is completed, but in fact you can also view the output of a build as it happens, with logs sent in real-time even from the agent running on your machine to be viewed via the Azure DevOps UI live!<\/p>\n\n\n\n<p>So, I&#8217;ve established that my build agent is able to successfully compile my Delphi code, even though Azure DevOps knows nothing about Delphi (other than in providing a default <strong>.gitignore<\/strong> for my repos).<\/p>\n\n\n\n<p class=\"has-drop-cap\">Next time we&#8217;ll skip ahead somewhat with a more comprehensive and sophisticated build script.  We&#8217;ll also do something more useful after a successful build: Executing some tests and capturing the results of those tests to obtain code quality metrics from Azure DevOps.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> In this post we create a (very!) simple project, build it using Delphi (7) and run it.  All with Azure DevOps.<\/p>\n","protected":false},"author":2,"featured_media":2862,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[324,322,323,4,321],"tags":[325,326,251,292],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/pipelines-hero-code-1024x256.jpg?fit=1024%2C256&ssl=1","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-K9","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":2919,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2919\/","url_meta":{"origin":2861,"position":0},"title":"Azure DevOps &#8211; Now You Too Can Use My Template(s)","date":"14 Sep 2019","format":false,"excerpt":"Learn how you too can use my Delphi build template in your own Azure DevOps pipelines, and a sneak preview of something special coming soon...","rel":"","context":"In &quot;automation&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/github-512.png?fit=512%2C512&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":2659,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2659\/","url_meta":{"origin":2861,"position":1},"title":"Azure DevOps and Delphi &#8211; Build Agents","date":"06 Sep 2019","format":false,"excerpt":"The first in a series of posts exploring build and test automation for Delphi projects using Azure DevOps.","rel":"","context":"In &quot;automation&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/pipelines-hero-1-1024x256.jpg?fit=1024%2C256&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":2936,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2936\/","url_meta":{"origin":2861,"position":2},"title":"Azure DevOps &#8211; Iterative Insertion Fixed!","date":"19 Sep 2019","format":false,"excerpt":"I figured out the iterative insertion problem and my build pipeline is now TIGHT! Fixing it was super-easy in fact, barely an inconvenience.","rel":"","context":"In &quot;automation&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/pipelines-hero-code-1024x256.jpg?fit=1024%2C256&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":2950,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2950\/","url_meta":{"origin":2861,"position":3},"title":"Azure DevOps &#8211; Great, But I Use GitHub!","date":"22 Sep 2019","format":false,"excerpt":"If you were thinking that Azure DevOps has nothing to offer if your code is in github, think again!","rel":"","context":"In &quot;Azure DevOps&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/github-512.png?fit=512%2C512&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":2968,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2968\/","url_meta":{"origin":2861,"position":4},"title":"Azure DevOps + GitHub = GitHub++","date":"23 Sep 2019","format":false,"excerpt":"A look at some of the integrations that \"just work\" when you combine Azure DevOps and GitHub","rel":"","context":"In &quot;automation&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/azure-devopsgithub.png?fit=1024%2C341&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":2878,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2878\/","url_meta":{"origin":2861,"position":5},"title":"Azure DevOps &#8211; Template for Builds + Running Tests and Capturing Results","date":"12 Sep 2019","format":false,"excerpt":"A more complete build script, re-usable in the form of a template, that caters for different Delphi versions, combined with a demonstration of running unit tests and capturing results for reporting and analysis in Azure DevOps Pipelines.","rel":"","context":"In &quot;automation&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-12-at-14.25.20.jpg?fit=386%2C386&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2861"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=2861"}],"version-history":[{"count":12,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2861\/revisions"}],"predecessor-version":[{"id":2906,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2861\/revisions\/2906"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media\/2862"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2861"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2861"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2861"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}