{"id":2919,"date":"2019-09-14T12:16:06","date_gmt":"2019-09-14T00:16:06","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2919"},"modified":"2019-09-22T10:00:03","modified_gmt":"2019-09-21T22:00:03","slug":"azure-devops-now-you-too-can-use-my-templates","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2919\/","title":{"rendered":"Azure DevOps &#8211; Now You Too Can Use My Template(s)"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">5<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span>\n<p>I&#8217;ve continued to enhance and refine my Delphi build template for Azure DevOps pipelines and am now hosting that in a new repository I&#8217;ve created on GitHub.  This means that anyone can now use this template in their own build pipelines.<\/p>\n\n\n\n<p>Here&#8217;s how.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p class=\"has-drop-cap\">In <a href=\"https:\/\/www.deltics.co.nz\/blog\/posts\/2878\">the previous post on this subject<\/a>, the template I created was in the same repo as the build pipeline that consumed it.  This made it very simple to re-use within that repo, but things get a bit more complicated when sharing between\/across repos.  In particular, templates in <em>Azure<\/em> repos can only be shared with other repos <em>within the same <\/em><strong><em>Organization<\/em><\/strong>.<\/p>\n\n\n\n<p>e.g. a build pipeline for a project in the <strong>ACME Organization<\/strong> may reference templates held in any other repo for any project <em>also in the <\/em><strong><em>ACME Organization<\/em><\/strong>.  But repos in projects that are part of the <strong><em>Spectre Organization<\/em><\/strong> cannot reference any of those <strong>ACME Organization<\/strong> templates.<\/p>\n\n\n\n<p>Clear ?<\/p>\n\n\n\n<p>In essence, it allows organizations to share templates among their own projects but not with other organizations.  Sharing templates privately in this way is of course a perfectly valid use case, but this doesn&#8217;t work for templates intended to be shared with a broader community. <\/p>\n\n\n\n<p>Fortunately, Azure DevOps pipelines also support referencing templates hosted on repositories other than Azure DevOps itself.  The only slight catch is that this only works if those repos are on GitHub (also <a href=\"https:\/\/blogs.microsoft.com\/blog\/2018\/10\/26\/microsoft-completes-github-acquisition\/\">now Microsoft owned<\/a>, in case you missed <a href=\"https:\/\/blogs.microsoft.com\/blog\/2018\/10\/26\/microsoft-completes-github-acquisition\/\">that piece of news<\/a>).<\/p>\n\n\n\n<p class=\"has-drop-cap\">To do this, the first thing you need to do this is create a <strong>Service Connection<\/strong> in your Azure Project.  This will be used to authenticate any requests to obtain templates held in GitHub repos.  This is done in Project Settings, in the Service Connections area:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" loading=\"lazy\" width=\"640\" height=\"428\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.24.54.png?resize=640%2C428&#038;ssl=1\" alt=\"\" class=\"wp-image-2920\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.24.54.png?resize=1024%2C684&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.24.54.png?resize=300%2C200&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.24.54.png?resize=768%2C513&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.24.54.png?resize=380%2C254&amp;ssl=1 380w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.24.54.png?w=1183&amp;ssl=1 1183w\" sizes=\"(max-width: 640px) 100vw, 640px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p><strong>GitHub<\/strong> is a specific service connection type recognised and supported by Azure DevOps.  When configuring the connection you need to specify a name and the authentication type.  I set mine up using a <strong>PAT<\/strong> (Personal Access Token).  The name is used in consuming pipelines so you&#8217;ll want to keep it short-ish, meaningful and memorable.<\/p>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\">If you reference a template that in turn references other GitHub hosted templates then you may need additional Service Connections with names that satisfy the endpoints configured in those references.<\/p>\n\n\n\n<p>To create the PAT itself you need to go to GitHub.  PAT&#8217;s are created and managed in the <strong>Developer Settings<\/strong> area of your GitHub profile:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" loading=\"lazy\" width=\"640\" height=\"226\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.30.30.png?resize=640%2C226&#038;ssl=1\" alt=\"\" class=\"wp-image-2921\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.30.30.png?resize=1024%2C362&amp;ssl=1 1024w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.30.30.png?resize=300%2C106&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.30.30.png?resize=768%2C272&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.30.30.png?resize=380%2C134&amp;ssl=1 380w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.30.30.png?w=1351&amp;ssl=1 1351w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2019-09-14-at-09.30.30.png?w=1280&amp;ssl=1 1280w\" sizes=\"(max-width: 640px) 100vw, 640px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>Now we can create a build pipeline that references my GitHub repo.  Here&#8217;s an example:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-yaml\">trigger:\n- develop\n- master\n\nresources:\n  repositories:\n    - repository: templates\n      type: github\n      name: deltics\/azure-pipeline-templates\n      endpoint: GitHubTemplates\n\npool:\n  name: &#039;The Den&#039;\n\nsteps:\n- template: delphi-build.yml@templates\n  parameters:\n    delphiVersion: 7\n    project: tests\\Tests\n    preBuildInline: duget restore --updateCfg\n    postBuildInline: .bin\\Tests -f=.results\\TestResults.xml<\/code><\/pre>\n\n\n\n<p>The most significant addition here compared to the earlier pipeline is the addition of the <code>resources:<\/code> section.  This is where we identify any resource this pipeline needs.  A GitHub repo is one of those and needs to be added in a <code>repositories:<\/code> section of the <strong>resources<\/strong>.<\/p>\n\n\n\n<p>Now, some of the names for the properties of a GitHub repository resource can be a little confusing, so bear with me&#8230;<\/p>\n\n\n\n<p>The <code>repository:<\/code> has a value which is the name by which this resource is referenced elsewhere in the pipeline.  In this case <strong>templates<\/strong>.<\/p>\n\n\n\n<p>We then identify the type of repository, which is <strong>github<\/strong>.  For an Azure repo we would use <strong>git<\/strong>.  <em>(It seems to me that <\/em><strong><em>azure<\/em><\/strong><em> or <\/em><strong><em>azuregit<\/em><\/strong><em> would have been a better choice here, but Microsoft clearly don&#8217;t agree &#8211; Ed)<\/em>.<\/p>\n\n\n\n<p>The <code>name:<\/code> for the repository identifies the GitHub repository itself, in the form <code>&lt;account&gt;\/&lt;repo&gt;<\/code>.  My GitHub account is <strong>deltics<\/strong> and the repo I created to host my template(s) is <strong>azure-pipeline-templates<\/strong>.  Hence the &#8216;name&#8217; for this repository is <code>deltics\/azure-pipeline-templates<\/code>.<\/p>\n\n\n\n<p>Finally, the <code>endpoint:<\/code> property identifies the <strong>Service Connection<\/strong> used to communicate with the GitHub repo.<\/p>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\">There is an additional optional property which may be set including a <code>ref:<\/code> property which can be used to &#8216;pin&#8217; the referenced template to a specific tagged version.  By default if no <code>ref:<\/code> is specified, <code>master\/HEAD<\/code> will be used (i.e. latest version). <\/p>\n\n\n\n<p>All of these properties, as well as other resource types, are explained further in <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/devops\/pipelines\/yaml-schema?view=azure-devops&amp;tabs=schema#repository-resource\">the Microsoft documentation<\/a>.<\/p>\n\n\n\n<p>The build step that consumes the template is also changed slightly from <a href=\"https:\/\/www.deltics.co.nz\/blog\/posts\/2878\">before<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-yaml\">steps:\n- template: delphi-build.yml@templates\n  parameters:\n    delphiVersion: 7\n    project: tests\\Tests\n    preBuildInline: duget restore --updateCfg\n    postBuildInline: .bin\\Tests -f=.results\\TestResults.xml<\/code><\/pre>\n\n\n\n<p>The template reference which previously simply referenced a path within the same repo, now identifies a file (<code>delphi-build.yml<\/code>) within a named resource (<code>@templates<\/code>).<\/p>\n\n\n\n<p>Everything else is just as before although much simplified in this case as a result of that <code>preBuildInline:<\/code> property.<\/p>\n\n\n\n<p><code>preBuildInline<\/code> and <code>postBuildInline<\/code> are some additional properties that I&#8217;ve added to my template.  These allow me to specify some Powershell script to be executed immediately <em>before<\/em> and immediately <em>after<\/em> the invocation of the compiler.  <code>postBuildInline<\/code> will only be executed if the build is successful.<\/p>\n\n\n\n<p><code>postBuild<\/code> still exists and functions as before, running as a separate step after the successful completion of the build step.  I&#8217;ve also added <code>preBuild<\/code> which is also a separate step which executes before the build step.<\/p>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\">The key distinction between these <code>Inline<\/code> parameters and the non-inline versions is that the inline parameters must be Powershell statements where-as the non-inline parameters are YAML steps which could be any valid build pipeline step, Powershell or otherwise. <\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p class=\"has-drop-cap\">The overall build in this case is greatly simplified by the <code>preBuildInline<\/code> command.  Where-as before I had a cumbersome step which manually constructed a search path and passed that in via parameters to the template, all of that work is replaced by a single command in the <code>preBuildInline<\/code> script.<\/p>\n\n\n\n<p>The <code>duget restore<\/code> command constructs the required paths and the <code>--updateCfg<\/code> switch additionally instructs the command to add those paths to any <code>dcc32.cfg<\/code> or <code>dcc64.cfg<\/code> that it may find.<\/p>\n\n\n\n<p class=\"has-background has-very-light-gray-background-color\">This is why this command must be performed as an <code>Inline<\/code> script.  It is only at the point, immediately before the compiler is invoked, that the build has created the initial dcc32\/dcc64 config file that this command can then update.<\/p>\n\n\n\n<p>The <code>duget<\/code> command is an <strong>exe<\/strong> pre-installed on the build machine.  It doesn&#8217;t just work out the required paths, it also ensures that any dependencies that my project has are available for compilation.<\/p>\n\n\n\n<p>As I <a href=\"https:\/\/www.deltics.co.nz\/blog\/posts\/2878\">teased last time<\/a>, if you&#8217;re at all familiar with <strong><a href=\"https:\/\/www.nuget.org\/\">nuget<\/a><\/strong> then you already have a good idea of what <strong>duget<\/strong> is all about.  This is what I am busy working on right now and will talk more about where this project is at in future posts.<\/p>\n\n\n\n<p class=\"has-drop-cap\">For now however, the build template itself is available for use by anyone so inclined.  It doesn&#8217;t rely on or make direct use of <strong>duget<\/strong>.  Just follow the above steps to reference my GitHub repo, pull this template into your pipelines and you&#8217;re good to go.<\/p>\n\n\n\n<p>Don&#8217;t forget, you will of course need <a href=\"https:\/\/www.deltics.co.nz\/blog\/posts\/2659\">a build agent connected to DevOps<\/a>.<\/p>\n\n\n\n<p>Let me know how you get on using the template or, better yet, contribute to the template yourself!<\/p>\n\n\n\n<p>Of particular interest would be testing for and supporting builds for FMX and non-Windows platforms.  These are not areas that I use Delphi for myself so wouldn&#8217;t be confident in tackling those aspects of the template.<\/p>\n\n\n\n<p>Enjoy! \ud83d\ude42<\/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\">5<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> 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&#8230;<\/p>\n","protected":false},"author":2,"featured_media":2922,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"Azure DevOps - Now You Too Can Use My Template(s).  This post also includes a sneak-peek at something I'm currently working which I'm very excited about!","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":[326,251,292,337,328],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/github-512.png?fit=512%2C512&ssl=1","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-L5","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":2936,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2936\/","url_meta":{"origin":2919,"position":0},"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":2919,"position":1},"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":2919,"position":2},"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":2861,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2861\/","url_meta":{"origin":2919,"position":3},"title":"Azure DevOps &#8211; Building Some Code","date":"09 Sep 2019","format":false,"excerpt":"In this post we create a (very!) simple project, build it using Delphi (7) and run it. All with 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-code-1024x256.jpg?fit=1024%2C256&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":2931,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2931\/","url_meta":{"origin":2919,"position":4},"title":"&#8216;Sorry, this script is too long&#8230;&#8217;","date":"18 Sep 2019","format":false,"excerpt":"An object lesson on the importance of defensive programming and providing helpful error messages to your users.","rel":"","context":"In &quot;Azure DevOps&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Mark_Twain_life_1900s.jpg?fit=883%2C331&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":2659,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2659\/","url_meta":{"origin":2919,"position":5},"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":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2919"}],"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=2919"}],"version-history":[{"count":7,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2919\/revisions"}],"predecessor-version":[{"id":2930,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2919\/revisions\/2930"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media\/2922"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2919"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2919"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2919"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}