How can I automatically create Azure DevOps Release Notes and how can I publish them?

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.
  • Wiki Updater - to upload a page tot a WIKI.
  • WIKI PDF Generator - to convert a generated page, or whole WIKI, to PDF format.

So lets deal with these tools in turn

Generating Release Notes

The Release Note task generates release notes by getting the items associated with a build (or release) from the Azure DevOps API and generating a document based on a Handlebars based template.

  • The artefacts that can be included in the release notes are details of the build/release and associated Work Items, Commits/Changesets, Tests and Pull Requests.
  • Most of the sample templates provided are for markdown format files. However, they could easily be converted for other text-based formats such as HTML if needed.
  • The use of Handlebars are the templating language makes for a very flexible and easily extensible means of document generation. There are sample of custom extensions provided with the templates

Sample YAML for this task is as follows, not it is using an inline template but it is possible to also load the template from a file path

 1 - task: richardfennellBM.BM-VSTS-XplatGenerateReleaseNotes.XplatGenerate-Release-Notes.XplatGenerateReleaseNotes@3
 2          displayName: 'Generate Release Notes'
 3          inputs:
 4            outputfile: '$(System.DefaultWorkingDirectory)inline.md'
 5            outputVariableName: OutputText
 6            templateLocation: InLine
 7            inlinetemplate: |
 8              # Notes for build 
 9              **Build Number**: {{buildDetails.id}}
10              **Build Trigger PR Number**: {{lookup buildDetails.triggerInfo 'pr.number'}} 
11
12              # Associated Pull Requests ({{pullRequests.length}})
13              {{#forEach pullRequests}}
14              {{#if isFirst}}### Associated Pull Requests (only shown if  PR) {{/if}}
15              *  **PR {{this.id}}**  {{this.title}}
16              {{/forEach}}
17
18              # Builds with associated WI/CS ({{builds.length}})
19              {{#forEach builds}}
20              {{#if isFirst}}## Builds {{/if}}
21              ##  Build {{this.build.buildNumber}}
22              {{#forEach this.commits}}
23              {{#if isFirst}}### Commits {{/if}}
24              - CS {{this.id}}
25              {{/forEach}}
26              {{#forEach this.workitems}}
27              {{#if isFirst}}### Workitems {{/if}}
28              - WI {{this.id}}
29              {{/forEach}} 
30              {{/forEach}}
31
32              # Global list of WI ({{workItems.length}})
33              {{#forEach workItems}}
34              {{#if isFirst}}## Associated Work Items (only shown if  WI) {{/if}}
35              *  **{{this.id}}**  {{lookup this.fields 'System.Title'}}
36                - **WIT** {{lookup this.fields 'System.WorkItemType'}} 
37                - **Tags** {{lookup this.fields 'System.Tags'}}
38              {{/forEach}}
39
40              {{#forEach commits}}
41              {{#if isFirst}}### Associated commits{{/if}}
42              * ** ID{{this.id}}** 
43                -  **Message:** {{this.message}}
44                -  **Commited by:** {{this.author.displayName}} 
45                -  **FileCount:** {{this.changes.length}} 
46              {{#forEach this.changes}}
47                    -  **File path (TFVC or TfsGit):** {{this.item.path}}  
48                    -  **File filename (GitHub):** {{this.filename}}  
49              {{/forEach}}
50              {{/forEach}}

How to Publish The Notes

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
  • Send the document as an email – I have used Rene van Osnabrugge Send Email Task for this job.
  • Upload it to a WIKI using my WIKI Updater Task
  • 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.

 1 - task: richardfennellBM.BM-VSTS-WIKIUpdater-Tasks.WikiUpdaterTask.WikiUpdaterTask@1
 2          displayName: 'Git based WIKI Updater'
 3          inputs:
 4            repo: 'dev.azure.com/richardfennell/Git%20project/_git/Git-project.wiki'
 5            filename: 'xPlatReleaseNotes/build-Windows-handlebars.md'
 6            dataIsFile: true
 7            sourceFile: '$(System.DefaultWorkingDirectory)inline.md'
 8            message: 'Update from Build'
 9            gitname: builduser
10            gitemail: 'build@demo'
11            useAgentToken: true

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.

In conclusion

So I hope that this post provides some ideas as to how you can use my tasks generate some useful release notes.