[Estimated Reading Time: 5 minutes]

I’ve continued to enhance and refine my Delphi build template for Azure DevOps pipelines and am now hosting that in a new repository I’ve created on GitHub. This means that anyone can now use this template in their own build pipelines.

Here’s how.

In the previous post on this subject, 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 Azure repos can only be shared with other repos within the same Organization.

e.g. a build pipeline for a project in the ACME Organization may reference templates held in any other repo for any project also in the ACME Organization. But repos in projects that are part of the Spectre Organization cannot reference any of those ACME Organization templates.

Clear ?

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’t work for templates intended to be shared with a broader community.

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 now Microsoft owned, in case you missed that piece of news).

To do this, the first thing you need to do this is create a Service Connection 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:

GitHub 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 PAT (Personal Access Token). The name is used in consuming pipelines so you’ll want to keep it short-ish, meaningful and memorable.

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.

To create the PAT itself you need to go to GitHub. PAT’s are created and managed in the Developer Settings area of your GitHub profile:

Now we can create a build pipeline that references my GitHub repo. Here’s an example:

trigger:
- develop
- master

resources:
  repositories:
    - repository: templates
      type: github
      name: deltics/azure-pipeline-templates
      endpoint: GitHubTemplates

pool:
  name: 'The Den'

steps:
- template: delphi-build.yml@templates
  parameters:
    delphiVersion: 7
    project: tests\Tests
    preBuildInline: duget restore --updateCfg
    postBuildInline: .bin\Tests -f=.results\TestResults.xml

The most significant addition here compared to the earlier pipeline is the addition of the resources: 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 repositories: section of the resources.

Now, some of the names for the properties of a GitHub repository resource can be a little confusing, so bear with me…

The repository: has a value which is the name by which this resource is referenced elsewhere in the pipeline. In this case templates.

We then identify the type of repository, which is github. For an Azure repo we would use git. (It seems to me that azure or azuregit would have been a better choice here, but Microsoft clearly don’t agree – Ed).

The name: for the repository identifies the GitHub repository itself, in the form <account>/<repo>. My GitHub account is deltics and the repo I created to host my template(s) is azure-pipeline-templates. Hence the ‘name’ for this repository is deltics/azure-pipeline-templates.

Finally, the endpoint: property identifies the Service Connection used to communicate with the GitHub repo.

There is an additional optional property which may be set including a ref: property which can be used to ‘pin’ the referenced template to a specific tagged version. By default if no ref: is specified, master/HEAD will be used (i.e. latest version).

All of these properties, as well as other resource types, are explained further in the Microsoft documentation.

The build step that consumes the template is also changed slightly from before:

steps:
- template: delphi-build.yml@templates
  parameters:
    delphiVersion: 7
    project: tests\Tests
    preBuildInline: duget restore --updateCfg
    postBuildInline: .bin\Tests -f=.results\TestResults.xml

The template reference which previously simply referenced a path within the same repo, now identifies a file (delphi-build.yml) within a named resource (@templates).

Everything else is just as before although much simplified in this case as a result of that preBuildInline: property.

preBuildInline and postBuildInline are some additional properties that I’ve added to my template. These allow me to specify some Powershell script to be executed immediately before and immediately after the invocation of the compiler. postBuildInline will only be executed if the build is successful.

postBuild still exists and functions as before, running as a separate step after the successful completion of the build step. I’ve also added preBuild which is also a separate step which executes before the build step.

The key distinction between these Inline 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.


The overall build in this case is greatly simplified by the preBuildInline 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 preBuildInline script.

The duget restore command constructs the required paths and the --updateCfg switch additionally instructs the command to add those paths to any dcc32.cfg or dcc64.cfg that it may find.

This is why this command must be performed as an Inline 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.

The duget command is an exe pre-installed on the build machine. It doesn’t just work out the required paths, it also ensures that any dependencies that my project has are available for compilation.

As I teased last time, if you’re at all familiar with nuget then you already have a good idea of what duget 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.

For now however, the build template itself is available for use by anyone so inclined. It doesn’t rely on or make direct use of duget. Just follow the above steps to reference my GitHub repo, pull this template into your pipelines and you’re good to go.

Don’t forget, you will of course need a build agent connected to DevOps.

Let me know how you get on using the template or, better yet, contribute to the template yourself!

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’t be confident in tackling those aspects of the template.

Enjoy! 🙂