Azure DevOps YAML base pipelines allow the pipeline definitions to be treated like any other code. So you make changes in a branch and PR them into the main/trunk when they are approved.
This works well if all the YAML files are in the same repo, but not so well if you are using YAML templates and the templated YAML is stored in a different repo. This is because an Azure DevOps PR is limited to a single repo. So testing a change to a YAML template in a different repo needs a bit of thought.
Say for example you have a template called core.yml in a repo called YAMLTemplates and you make a change to it and start a PR. Unless you have a test YAML pipeline in that repo, which is not a stupid idea, but not always possible depending on the complexity of your build process, there is no way to test the change inside that repo.
The answer is to create a temporary branch in a repo that consumes the shared YAML template. In this temporary branch make an edit to the repository setting that references the shared YAML repo to point to the update branch contain the PR
A question I am often asked when consulting on Azure DevOps is ‘how can I automatically create release notes and how can I publish them?’.
Well it is for just this requirement that I have written a set of Azure DevOps Pipeline Tasks
Release Note Generator – to generate release notes. I strongly recommend this Cross-platform Node-based version. I plan to deprecate my older PowerShell version in the not too distant future as it uses ‘homegrown logic’, as opposed to standard Azure DevOps API calls, to get associated items.
Once the document has been generated there is a need for a decision as to how to publish it. TThere are a few options
Attach the markdown file as an artefact to the Build or Pipeline. Note you can’t do this with a UI based Releases as they have no concept of artefacts, but this is becoming less of a concern as people move to multistage YAML.
Save in some other location e.g Azure Storage or if on-premises a UNC file share
Convert the markdown release note document, or the whole WIKI, to a PDF and use any of the above options using first my WIKI PDF Exporter Task then another task.
I personally favour the 1st and 4th options used together. Attachment to the pipeline and then upload the document to a WIKI
A sample of suitable YAML is shown below, uploading the document to an Azure DevOps WIKI. Please note that the repo URL and authentication can trip you up here so have a good read of the provided documentation before you use this task.
- task: richardfennellBM.BM-VSTS-WIKIUpdater-Tasks.WikiUpdaterTask.WikiUpdaterTask@1
displayName: 'Git based WIKI Updater'
message: 'Update from Build'
But when do I generate the release notes?
I would suggest you always generate release notes every build/pipeline i.e. a document of the changes since the last successful build/pipeline of that build definition. This should be attached as an artefact.
However, this per build document will usually too granular for use as ‘true’ release notes i.e. something to hand to a QA team, auditor or client.
To address this second use case I suggest, within a multistage YAML pipeline (or a UI based release), having a stage specifically for generating release notes.
My task has a feature that it will check for the last successful release of a pipeline/release to the stage it is defined in, so will base the release note on the last successful release to that given stage. If this ‘documentation’ stage is only run when you are doing a ‘formal’ release, the release note generated will be since the last formal release. Exactly what a QA team or auditor or client might want.
So I hope that this post provides some ideas as to how you can use my tasks generate some useful release notes.
I have an Azure DevOps multi-stage YAML pipeline that started giving the error `The pipeline is not valid error: Unable to resolve latest version for pipeline templates: this could be due to inaccessible pipeline or no version is available` and failing instantly.
This is not the most helpful message, but after some digging I found the problem.
In the past, the answer was that I did not know of any easy way. However, I have recently come across a command line tool by Max Melcher called AzureDevOps.WikiPDFExport that allows you to export a whole WIKI (or a single file) as a PDF. Its basic usage is
Clone a WIKI Repo
Run the command line tool passing in a path to the root of the cloned repo
The .order file is read
A PDF is generated
This is a nice and simple process, but it would be nice to be able to automate this process as part of a build pipeline.
After a bit of thought, I realised I had much of the code I needed to automated the process in my WIKIUpdater extension as these tasks are based around cloning repos.
Disks filling up on our private Azure DevOps agents is a constant battle. We have maintenance jobs setup on the agent pools, to clean out old build working folders nightly, but these don’t run often enough. We need a clean out more than once a day due to the number and size of our builds.
To address this, with UI based builds, we successfully used the Post Build Cleanup Extension. However since we have moved many of our builds to YAML we found it not working so well. Turned out the problem was due to the way got source code.
The Post Build Cleanup task is intelligent, it does not just delete folders on demand. It check to see what the Get Source ‘Clean’ setting was when the repo was cloned and bases what it deletes on this value e.g. nothing, source, or everything. This behaviour is not that obvious.
In a UI based builds it is easy to check this setting. You are always in the UI when editing the build. However, in YAML it is easy to forget the setting, as it is one of those few values that cannot be set in YAML.
To make the post build cleanup task actually delete folders in a YAML pipeline you need to
Edit the pipeline
Click the ellipse menu top right
Pick YAML and select the ‘Get Source’ block
Make sure the ‘Clean’ setting is set to ‘true’ and the right set of items to delete are selected – if this is not done the post clean up task does nothing
You can then add the post build cleanup task the end of the steps
- script: echo This where you do stuff
- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3
displayName: 'Clean Agent Directories'
My local wireless environment is now very congested, I assume as more people are working from home.
Both the 2.4GHz and 5Ghz network were on the same channels as other strong signals.
Also they were using the same SSID, which is meant to provide seamless swap-over between 2.4 and 5Ghz. But, in reality this meant there were connection problems as a connection flipped between frequencies.
This explained other problems I had seem
The Microsoft Direct Access VPN I use to connect to the office failing intermittently. Obviously, any problems I have connecting to the office to do work is far less important than Zwift connection issues.
My Samsung phone would drop calls for no reason. I now think this was when it had decided to use Wifi calling and got confused over networks. Note: I had fixed this by switching off Wifi calling.
To address the problems I changed the SSIDs so that my 2.4 and 5Ghz networks had different names, so that I know which one I was using. Also I moved the channels to ones not used by my neighbours
Put the phone and the PC on the 2.4Ghz network
No improvement, app did not work and PC slow to save
Put the phone and the PC on 5Ghz
Small improvement, app still did not work but at least tried to show the in game view before it dropped out. The PC was still slow to save
So it seems the problem was upload speed from my PC all along. Strange as I would have expected the 5Ghz network to be fine, even if the 2.4Ghz was not. The 5Ghz Wifi seems to perform OK on a speed test.
Anyway it is working now, but maybe it is time to consider a proper mesh network?
This is one of those blog posts I write to remind my future self how I fixed a problem.
I have a release that installs VSTest and runs some integration tests that target .NET 4.6 x64. All these tests worked fine in Visual Studio. However, I got the following errors for all tests when they were run in a release
2020-04-23T09:30:39.2634578Z Starting test execution, please wait...
2020-04-23T09:30:39.4783658Z A total of 1 test files matched the specified pattern.
2020-04-23T09:30:40.8660112Z X Can_Get_MIDs [521ms]
2020-04-23T09:30:40.8684249Z Error Message:
2020-04-23T09:30:40.8684441Z Test method PaymentServices.IntegrationTests.ControllerMIDTests.Can_Get_MIDs threw exception:
2020-04-23T09:30:40.8684574Z System.BadImageFormatException: Could not load file or assembly 'PaymentServices, Version=188.8.131.52, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An attempt was made to load a program with an incorrect format.
2020-04-23T09:30:40.8684766Z Stack Trace:
2020-04-23T09:30:40.8684881Z at PaymentServices.IntegrationTests.ControllerMIDTests.Can_Get_MIDs()
Well now I consider that multistage YAML pipelines are mature enough to allow me to do my whole release pipeline in YAML, hence this post.
My pipeline performs a number of stages, you can find a sample pipeline here. Note that I have made every effort to extract variables into variable groups to aid reuse of the pipeline definition. I have added documentation as to where variable are stored and what they are used for.
The stages are as follows
The build phase does the following
Updates all the TASK.JSON files so that the help text has the correct version number
Calls a YAML template (build-Node-task) that performs all the tasks to transpile a TypeScript based task – if my extension contained multiple tasks this template would be called a number of time
Get NPM packages
Run Snyk to check for vulnerabilities – if any vulnerabilities are found the build fails
Lint and Transpile the TypeScript – if any issue are found the build fails
Run any Unit test and publish results – if any test fail the build fails
Package up the task (remove dev dependencies)
Download the TFX client
Package up the Extension VSIX package and publish as a pipeline artifact.
This phase is done as a deployment job and is linked to an environment,. However, there are no special approval requirements are set on this environment. This is because I am happy for the release to be done to the private instance assuming the build phase complete without error.
This is where the pipeline gets interesting. The test phase does the following
Runs any integration tests. These could be anything dependant on the extension being deployed. Unfortunately there is no option at present in multistage pipeline for a manual task to say ‘do the manual tests’, but you could simulate similar by sending an email or the like.
The clever bit here is that I don’t want this stage to run until the new private version of the extension has been published and is available; there can be a delay between TFX saying the extension is published and it being downloadable by an agent. This can cause a problem in that you think you are running tests against a different version of the extension to one you have. To get around this problem I have implemented a check on the environment this stage’s deployment job is linked to. This check runs an Azure Function to check the version of the extension in the Marketplace. This is exactly the same Azure Function I already used in my UI based pipelines to perform the same job.
The only issue here is that this Azure Function is used as an exit gate in my UI based pipelines; to not allow the pipeline to exit the private stage until the extension is publish. I cannot do this in a multistage YAML pipeline as environment checks are only done on entry to the environment. This means I have had to use an extra Test stage to associate the entry check with. This was setup as follows
Create a new environment
Click the ellipse (…) and pick ‘approvals and checks’
Add a new Azure Function check
Provide the details, documented in my previous post, to link to your Azure Function. Note that you can, in the ’control options’ section of the configuration, link to a variable group. This is a good place to store all the values, you need to provide
URL of the Azure Function
Key to us the function
The function header
The body – this one is interesting. You need to provide the build number and the GUID of a task in the extension for my Azure Function. It would be really good if both of these could be picked up from the pipeline trying to use the environment. This would allow a single ‘test’ environment to be created for use by all my extensions, in the same way there are only a single ‘private’ and ‘public’ environment. However, there is a problem, the build number is picked up OK, but as far as I can see I cannot access custom pipeline variables, so cannot get the task GUID I need dynamically. I assume this is because this environment entry check is run outside of the pipeline. The only solution can find is to place the task GUID as a hard coded value in the check declaration (or I suppose in the variable group). The downside of this is it means I have to have an environment dedicated to each extension, each with a different task GUID. Not perfect, but not too much of a problem
In the Advanced check the check logic
In control options link to the variable group contain any variables used.
It took a bit of trial and error to get this going, but I think I have a good solution now. The fact that the bulk of the work is done using shared templates means I should get good reuse of the work I have done. I am sure I will be able to improve the template as time goes on but it is a good start