NML

Azure DevOps Release Notes Guide

Release notes is a document that contains updates and/or fixes that went in as part of a release. It is a way to communicate the evolution of a product to all stakeholders. Apart from its value to business, we can see it as a celebration of the team’s work and a story of a product’s growth.

As part of the aim to standardize the development process, we want to implement auto-generating of release notes. Creating documentation is not always easy or efficient, so having it part of your CI/CD process is crucial.

Assumptions

You will need to meet the following assumptions need to in order to generate release notes in Azure DevOps.

  • You log and manage all development tickets in Azure DevOps.
  • At least one Build has been successfully setup in Azure DevOps.
  • At least one Release has been successfully setup in Azure DevOps.
  • A Wiki has been successfully setup in Azure DevOps (if you want the Wiki to be the target, highly recommended).

Considerations

You need to consider the following in order to generate release notes in Azure DevOps.

  • You have well written titles for your work items/tickets in Azure DevOps.
  • You have well written descriptions for your work items/tickets in Azure DevOps.
  • You link your associated work items to one of the following (ordered by preference, for ease of process):
    • Development branches
    • Pull requests
    • Build
  • You do not have any tags on development tickets. (Existing tags may lead to unexpected behavior)

How-to’s

Link work items to development items

Option 1: From the work item

When looking at the details of a work item, click on the ‘Add link’ option under the Development section. An Add link pop up will show, where you can configure the type of link to add.

Below is the extensive list of link type:

Option 2: From a pull request

When creating a new pull request, there is a section to link work items. You can use this, if you have not used option 1 and create a policy to make this field required.

Note: If you have followed Option 1, the items linked to the source branch when creating the PR will show up in the list.

Linking work items to builds

There is simply an option to enables in your Build Pipeline, namely “Automatically link new work in this build”.

Once the build is complete you will see a new Development link on your work item pop up, called “Integrated in Build”

Note: You can add this link after the fact, if you missed linking a work item before building and want it in your release notes.

Once your build complete you will see in the summary a list of link work items. These items will eventually show up on the release notes. I like to do a sanity check that all of the necessary fields are filled in correctly.

Tag work items for release notes to identify (optional)

By default, the release notes task will pull the information for all linked work items and write it to the notes. If you need to filter out some items (dev tasks, tech debt etc.) or you want to categories your items (into updates, feature, bugs etc.) you can make use of tags.

When the release notes task loops through the linked items, you can tell it to only process items with a specific tag. We use a plugin called WorkItem Updater to do this automatically as part of our Release pipeline. Currently, we write User Stories information under an Updates heading and Bugs information under a Fixes heading.

You can also make use of an environment variable in order to add a tag which correlates to the environment where the work item is deployed to as illustrated below

If you would like to implement tagging for release notes, this task should run before generating the release notes as described in the next section.

env variable

Use the provided Azure DevOps Pipeline Task Group

A task group export is provided at the end of this article. Save it to Create And Publish Release Notes.json and import into your project task groups. You can skip the next 2 sections if you use the task group.

Manually add tasks for generating release notes and updating to the wiki

Create task to generate release notes

Using a plugin called Generate Release Notes XPlatform; create a task in your release pipeline. We manually trigger a separate Stage for generating release notes, since (for now) we do not want to create release notes on every release.

There are 2 important options here:

  1. Output file: This is a ‘temporary’ file for storing the release notes.
  2. Template location: Either a File or InLine. The choice does not matter, but the format of the file needs to be in MarkDown and make use of some syntax.

Below is a preview of the template that we use on 10X, highlighted is the ‘special’ syntax found in the docs for the plugin (https://marketplace.visualstudio.com/items?itemName=richardfennellBM.BM-VSTS-XplatGenerateReleaseNotes).

Once this task completes, you will have successfully generated a set of release notes. You can now make use of the output file in anyway you please. Use a task to download or email it or follow the next step to use it to update your Wiki.

Note: To find the ‘technical’ name of a work item field go to https://docs.microsoft.com/en-us/rest/api/azure/devops/wit/fields/list?view=azure-devops-rest-5.0.

Update wiki with release notes (optional)

Using a plugin called GIT based WIKI Updater; create a task in your release pipeline. The important options to note here are:

  1. Repo: The task pushes the file to this repository. If you did not explicitly create a git repository for your project wiki, but you have a create a wiki for your project from the UI, the URL for your default project wiki is https://dev.azure.com/NewMediaLabs/<project_name>/_git/<project_name>.wiki. You can test this by browsing to it in your browser.
  2. Name of the file/page: The task will create the folder structure and file for the release notes defined here.
  3. Data is a file: Enable this option
  4. Source file: Use the same path and file as Output file in the previous task.
  5. Authentication: Use Run with Build Agent Credentials. See the section for "Build Agent Permissions" below to ensure this configuration works

Add to wiki settings

![Set "Allow scripts to access OAuth token" on the Agent job](/static/images/allow-scripts-to-access-oauth-token.png "Set \\"Allow scripts to access OAuth token\\" on the Agent job")

Build Agent Permissions

  1. Set "Allow scripts to access OAuth token" on the "Agent job".
  2. Set the appropriate permissions on the wiki repository for the build agent (or all repositories).
    1. `Contribute` must be set to `Allow`

Build agent appropriate repository permissions

Release Notes Template Markdown


## **Release notes**
| | RELEASE | BUILD |
|---------|---------|-------|
| Definition Name | ${releaseDetails.releaseDefinition.name} | ${releaseDetails.artifacts[0].definitionReference.definition.name} |
| Current Name | ${releaseDetails.name} | ${releaseDetails.artifacts[0].definitionReference.version.name} |
| Completed | ${releaseDetails.modifiedOn} | N/A |
| Source Branch | N/A | ${releaseDetails.artifacts[0].definitionReference.branch.name} | ## **User Stories**
@@WILOOP:$(DevEnvironmentTag); USERSTORY@@
#${widetail.id}
${widetail.fields['System.Title']}
${widetail.fields['Microsoft.VSTS.Common.AcceptanceCriteria']}
---

@@WILOOP:$(DevEnvironmentTag); USERSTORY@@
## **Fixes**
@@WILOOP:$(DevEnvironmentTag); BUG@@
#${widetail.id}
${widetail.fields['System.Title']}
${widetail.fields['Microsoft.VSTS.TCM.SystemInfo']}
---

@@WILOOP:$(DevEnvironmentTag); BUG@@
## **Source code commits**
@@CSLOOP@@
* ${csdetail.message}
**Commit Id:** ${csdetail.id}
---

@@CSLOOP@@

Task Group Template for Tagging Work Items

{"tasks":[{"environment":{},"displayName":"Tag User Stories for Release Notes","alwaysRun":false,"continueOnError":false,"condition":"succeeded()","enabled":true,"timeoutInMinutes":0,"inputs":{"workitemsSource":"Build","workitemsSourceQuery":"","workItemType":"User Story","allWorkItemsSinceLastRelease":"true","workItemState":"Ready for Testing","workItemCurrentState":"","workItemKanbanLane":"","workItemKanbanState":"","workItemDone":"false","linkBuild":"true","updateAssignedTo":"Never","updateAssignedToWith":"Requester","assignedTo":"","bypassRules":"false","failTaskIfNoWorkItemsAvailable":"false","updateFields":"","addTags":"$(EnvironmentTag)\nUSERSTORY","removeTags":""},"task":{"id":"7164116a-ed17-48be-9c53-b440b2b1dd2e","versionSpec":"2.*","definitionType":"task"}},{"environment":{},"displayName":"Tag Bugs for Release Notes","alwaysRun":false,"continueOnError":false,"condition":"succeeded()","enabled":true,"timeoutInMinutes":0,"inputs":{"workitemsSource":"Build","workitemsSourceQuery":"","workItemType":"Bug","allWorkItemsSinceLastRelease":"true","workItemState":"Resolved","workItemCurrentState":"","workItemKanbanLane":"","workItemKanbanState":"","workItemDone":"false","linkBuild":"true","updateAssignedTo":"Never","updateAssignedToWith":"Requester","assignedTo":"","bypassRules":"false","failTaskIfNoWorkItemsAvailable":"false","updateFields":"","addTags":"$(EnvironmentTag)\nBUG","removeTags":""},"task":{"id":"7164116a-ed17-48be-9c53-b440b2b1dd2e","versionSpec":"2.*","definitionType":"task"}}],"runsOn":["Agent","DeploymentGroup"],"revision":1,"createdBy":{"displayName":"Njinu Kimani","id":"26bd5f4b-891a-6d15-a0a4-ef5131b6ed7f","uniqueName":"njinu@nml.co.za"},"createdOn":"2019-10-02T14:24:14.430Z","modifiedBy":{"displayName":"Njinu Kimani","id":"26bd5f4b-891a-6d15-a0a4-ef5131b6ed7f","uniqueName":"njinu@nml.co.za"},"modifiedOn":"2019-10-02T14:24:14.430Z","id":"599155a1-2cc0-4557-b73d-a5c8563c8e1e","name":"Tag Work Items for Release Notes","version":{"major":1,"minor":0,"patch":0,"isTest":false},"iconUrl":"https://cdn.vsassets.io/v/M155_20190805.4/_content/icon-meta-task.png","friendlyName":"Tag Work Items for Release Notes","description":"","category":"Deploy","definitionType":"metaTask","author":"Charl Marais","demands":[],"groups":[],"inputs":[{"aliases":[],"options":{},"properties":{},"name":"EnvironmentTag","label":"EnvironmentTag","defaultValue":"","required":true,"type":"string","helpMarkDown":"The  environment tag to add to identified work items","groupName":""}],"satisfies":[],"sourceDefinitions":[],"dataSourceBindings":[],"instanceNameFormat":"Task group: Tag Work Items for Release Notes $(EnvironmentTag)","preJobExecution":{},"execution":{},"postJobExecution":{}}

Task Group Template for Generating Release Notes

{"tasks":[{"environment":{},"displayName":"Tag User Stories for Release Notes","alwaysRun":false,"continueOnError":false,"condition":"succeeded()","enabled":true,"timeoutInMinutes":0,"inputs":{"workitemsSource":"Build","workitemsSourceQuery":"","workItemType":"User Story","allWorkItemsSinceLastRelease":"true","workItemState":"Closed","workItemCurrentState":"Accepted,Active,Closed,Completed,Design,Impeded,In Code Review,In Planning,In Progress,Inactive,New,Ready,Ready for Testing,Removed,Requested,Resolved","workItemKanbanLane":"","workItemKanbanState":"","workItemDone":"false","linkBuild":"true","updateAssignedTo":"Never","updateAssignedToWith":"Requester","assignedTo":"","bypassRules":"false","failTaskIfNoWorkItemsAvailable":"false","updateFields":"","addTags":"$(EnvironmentTag)\nUSERSTORY","removeTags":""},"task":{"id":"7164116a-ed17-48be-9c53-b440b2b1dd2e","versionSpec":"2.*","definitionType":"task"}},{"environment":{},"displayName":"Tag Bugs for Release Notes","alwaysRun":false,"continueOnError":false,"condition":"succeeded()","enabled":true,"timeoutInMinutes":0,"inputs":{"workitemsSource":"Build","workitemsSourceQuery":"","workItemType":"Bug","allWorkItemsSinceLastRelease":"true","workItemState":"Closed","workItemCurrentState":"Accepted,Active,Closed,Completed,Design,Impeded,In Code Review,In Planning,In Progress,Inactive,New,Ready,Ready for Testing,Removed,Requested,Resolved","workItemKanbanLane":"","workItemKanbanState":"","workItemDone":"false","linkBuild":"true","updateAssignedTo":"Never","updateAssignedToWith":"Requester","assignedTo":"","bypassRules":"false","failTaskIfNoWorkItemsAvailable":"false","updateFields":"","addTags":"$(EnvironmentTag)\nBUG","removeTags":""},"task":{"id":"7164116a-ed17-48be-9c53-b440b2b1dd2e","versionSpec":"2.*","definitionType":"task"}},{"environment":{},"displayName":"Generate Release Notes based on Release Comparision API","alwaysRun":false,"continueOnError":false,"condition":"succeeded()","enabled":true,"timeoutInMinutes":0,"inputs":{"outputfile":"$(System.DefaultWorkingDirectory)\\$(ReleaseNotesTempFileName)","outputVariableName":"","templateLocation":"InLine","templatefile":"# **Release notes for My Test**  |         | RELEASE | BUILD | |---------|---------|-------| | Definition Name | ${releaseDetails.releaseDefinition.name} | ${releaseDetails.artifacts[0].definitionReference.definition.name} | | Current Name | ${releaseDetails.name} |  ${releaseDetails.artifacts[0].definitionReference.version.name} | | Completed | ${releaseDetails.modifiedOn} | N/A | | Source Branch | N/A | ${releaseDetails.artifacts[0].definitionReference.branch.name} |                  ## **Description** @@WILOOP:RN-OVERVIEW@@ ${widetail.fields['System.Description']} @@WILOOP:RN-OVERVIEW@@  ## **Updates** @@WILOOP:Deployed to UAT; RN-UPDATES@@ #${widetail.id} ${widetail.fields['System.Title']}  ${widetail.fields['Microsoft.VSTS.Common.AcceptanceCriteria']}  ---  @@WILOOP:Deployed to UAT; RN-UPDATES@@  ## **Fixes** @@WILOOP:Deployed to UAT; RN-BUG@@ #${widetail.id} ${widetail.fields['System.Title']}  ${widetail.fields['Microsoft.VSTS.TCM.SystemInfo']}  ---  @@WILOOP:Deployed to UAT; RN-BUG@@  ## **Source code commits** @@CSLOOP@@ * ${csdetail.message}  **Commit Id:** ${csdetail.id}  ---  @@CSLOOP@@","inlinetemplate":"# **Release notes for My Test**\n\n|         | RELEASE | BUILD |\n|---------|---------|-------|\n| Definition Name | ${releaseDetails.releaseDefinition.name} | ${releaseDetails.artifacts[0].definitionReference.definition.name} |\n| Current Name | ${releaseDetails.name} |  ${releaseDetails.artifacts[0].definitionReference.version.name} |\n| Completed | ${releaseDetails.modifiedOn} | N/A |\n| Source Branch | N/A | ${releaseDetails.artifacts[0].definitionReference.branch.name} |                \n\n## **Description**\n@@WILOOP:RN-OVERVIEW@@\n${widetail.fields['System.Description']}\n@@WILOOP:RN-OVERVIEW@@\n\n## **User Stories**\n@@WILOOP:$(EnvironmentTag); USERSTORY@@\n#${widetail.id}\n${widetail.fields['System.Title']}\n\n${widetail.fields['Microsoft.VSTS.Common.AcceptanceCriteria']}\n\n---\n\n@@WILOOP:$(EnvironmentTag); USERSTORY@@\n\n## **Fixes**\n@@WILOOP:$(EnvironmentTag); BUG@@\n#${widetail.id}\n${widetail.fields['System.Title']}\n\n${widetail.fields['Microsoft.VSTS.TCM.SystemInfo']}\n\n---\n\n@@WILOOP:$(EnvironmentTag); USERSTORY@@\n\n## **Source code commits**\n@@CSLOOP@@\n* ${csdetail.message}\n\n**Commit Id:** ${csdetail.id}\n\n---\n\n@@CSLOOP@@","emptySetText":"None","delimiter":":","overrideStageName":"","stopOnRedeploy":"False"},"task":{"id":"7b0a5887-75c4-4ade-a915-f92a93c934cb","versionSpec":"2.*","definitionType":"task"}},{"environment":{},"displayName":"Git based WIKI Updater","alwaysRun":false,"continueOnError":false,"condition":"succeeded()","enabled":true,"timeoutInMinutes":0,"inputs":{"repo":"dev.azure.com/NewMediaLabs/highfive.nml.co.za/_git/highfive.nml.co.za.wiki","filename":"ReleaseNotes/$(EnvironmentFolderName)/ReleaseNotes_$(Release.ReleaseName).md","dataIsFile":"true","contents":"","sourceFile":"$(System.DefaultWorkingDirectory)\\$(ReleaseNotesTempFileName)","message":"Release notes for release $(Release.ReleaseName)","gitname":"$(Release.RequestedFor)","gitemail":"$(Release.RequestedForEmail)","useAgentToken":"true","user":"","password":"","localpath":"$(System.DefaultWorkingDirectory)\\repo"},"task":{"id":"e59c5ae6-3afd-479d-bc40-81cd6c541840","versionSpec":"1.*","definitionType":"task"}}],"runsOn":["Agent","DeploymentGroup"],"revision":7,"createdBy":{"displayName":"Charl Marais","id":"14acbb30-41ae-6ff8-adda-d29035dfc7ac","uniqueName":"charl@nml.co.za"},"createdOn":"2019-07-18T08:37:51.473Z","modifiedBy":{"displayName":"Charl Marais","id":"14acbb30-41ae-6ff8-adda-d29035dfc7ac","uniqueName":"charl@nml.co.za"},"modifiedOn":"2019-07-18T14:04:12.030Z","comment":"","id":"57d3e80a-2cdd-4af4-bd01-0dd56d4bf628","name":"Create And Publish Release Notes","version":{"major":1,"minor":0,"patch":0,"isTest":false},"iconUrl":"https://cdn.vsassets.io/v/M154_20190716.1/_content/icon-meta-task.png","friendlyName":"Create And Publish Release Notes","description":"","category":"Deploy","definitionType":"metaTask","author":"Charl Marais","demands":[],"groups":[],"inputs":[{"aliases":[],"options":{},"properties":{},"name":"EnvironmentFolderName","label":"EnvironmentFolderName","defaultValue":"","required":true,"type":"string","helpMarkDown":"The wiki folder name for environment where release note should be save to. It cannot contain spaces or special charaters.","groupName":""},{"aliases":[],"options":{},"properties":{},"name":"EnvironmentTag","label":"EnvironmentTag","defaultValue":"","required":true,"type":"string","helpMarkDown":"The tag to use on WorkiItems for the environment","groupName":""},{"aliases":[],"options":{},"properties":{},"name":"ReleaseNotesTempFileName","label":"ReleaseNotesTempFileName","defaultValue":"ReleaseNotes.md","required":true,"type":"string","helpMarkDown":"A temp file name where the release notes are saved.","groupName":""}],"satisfies":[],"sourceDefinitions":[],"dataSourceBindings":[],"instanceNameFormat":"Task group: Create And Publish Release Notes $(EnvironmentFolderName)","preJobExecution":{},"execution":{},"postJobExecution":{}}