[HOWTO] Automate release notes generation with an Azure DevOps YAML pipeline

In this blog post, I’ll demonstrate how to create an Azure DevOps YAML pipeline that automatically generates a new wiki page for each production release that lists the solved work items since the last release.

But first let’s take a look at the why. Why would you want to have automatically generated release notes if you already have an overview of work items per sprint / iteration? Usually because releases and deployments to production environment may do not always take place after each sprint but sometimes after several sprints. Furthermore a consistent structure is guaranteed.

Preconditions and assumptions

Next, be aware of the following preconditions and assumptions.

  • The Azure DevOps project is of type Agile or of a type that extends type Agile
  • Boards, Repos and Pipelines are enabled on the Azure DevOps project
  • Git is used as version control system
  • Work items (user stories and/or tasks) are linked in pull requests
  • A project wiki (not a code wiki!) is already initialized on Azure DevOps project
  • The user that sets up and configures the automated release notes generation is in role Project Administrator

Generate Release Notes (Crossplatform) extension

The release notes file which will be added as a sub page to the Azure DevOps project wiki, is created using the Generate Release Notes (Crossplatform) Azure DevOps extension from Visual Studio Marketplace.

A big thank you at this point to Richard Fennell, the author of the extension!

Implement release notes YAML pipeline

And now let’s dig into the effective solution – the Azure DevOps YAML pipeline itself which will be triggered on every commit to branch main / master as illustrated here.

The release notes pipeline presented in this blog post consists of the following three parts.

  • pipeline file
  • job file(s)
  • template file

First, create a new YAML pipeline file in the git repository (i.e. deploy\pipelines\release-notes-pipeline.yml).

name: $(Date:yyyyMMdd)$(Rev:.r) # defines the format of the build number
trigger:
  branches:
    include:
      - main # trigger on commits to branch 'main'
variables:
  - name: "isMainBranch"
    value: $[eq(variables['Build.SourceBranch'], 'refs/heads/main')]
pool:
  vmImage: "windows-latest"
stages:
  - stage: Prod
    displayName: "Release Notes and git tag for stage Production"
    condition: eq(variables.isMainBranch, true) # ensure conditional execution of stage
    jobs:
      - template: jobs-generate-release-notes.yml
      - template: jobs-create-git-tag.yml

Next, create the jobs file which is responsible for generating the release notes in the git repository.

deploy\pipelines\jobs-generate-release-notes.yml

jobs:
  - job: GenerateReleaseNotes
    displayName: "Generate Release Notes"
    steps:
      - task: XplatGenerateReleaseNotes@4 # version 4 is the latest one
        inputs:
          outputfile: "$(Build.ArtifactStagingDirectory)/releasenotes.md"
          templateLocation: "File" # File means the template is loaded from the path specified under 'templatefile'
          templatefile: "deploy/pipelines/release-notes-template.md"
          checkStage: true # generate release notes since last successful release
          stopOnRedeploy: true
          dumpPayloadToConsole: false
          dumpPayloadToFile: false
          replaceFile: True
          getParentsAndChildren: True # get parents and children of linked work items which will be accessible in the template
          getAllParents: true # get all parents of linked work items which will be accessible in the template
          getIndirectPullRequests: False
          stopOnError: False
          emptySetText: "No items found" # text to be returned if no work items found
      - task: PublishBuildArtifacts@1
        inputs:
          PathtoPublish: "$(Build.ArtifactStagingDirectory)/releasenotes.md" # path to publish release notes to
          ArtifactName: "release-notes"
          publishLocation: "Container"
      - bash: az devops configure --defaults organization=$(System.TeamFoundationCollectionUri) project=$(System.TeamProject) --use-git-aliases true
        displayName: "Set default Azure DevOps organization and project"
      - bash: az devops wiki page create --path 'Release Notes/$(Build.BuildNumber)' --wiki 'BFU.wiki' --file-path '$(Build.ArtifactStagingDirectory)/releasenotes.md'
        displayName: "Create wiki page for release notes"
        env:
          AZURE_DEVOPS_EXT_PAT: $(System.AccessToken)

For having a consistent git history, a git tag gets created in the following jobs file.

NOTE
In general I prefer to version releases according to semantic versioning (SemVer). However, in this example I used the current date and the revision number for simplicity.

deploy\pipelines\jobs-create-git-tag.yml

jobs:
  - job: CreateGitTag
    displayName: "Create git tag"
    steps:
      - checkout: self
        persistCredentials: true
        clean: true
      - script: |
          git config --global user.name "Azure DevOps"
          git config --global user.email "AZ_DEVOPS_ORG_NAME@YOURDOMAIN"
          git tag -a $(Build.BuildNumber) -m $(Build.BuildNumber)
          git push origin $(Build.BuildNumber)

Last but not least, add a handlebars template (file of type markdown) to the repository that defines the output.

deploy\pipelines\release-notes-template.md

# Release Notes
**Year:** {{year}}
**Stage:** {{currentStage.identifier}}
**Date & Time:** {{buildDetails.startTime}}
**Version:** {{buildDetails.buildNumber}}

## Features
{{#if this.relatedWorkItems}}
{{#forEach this.relatedWorkItems}}
{{#if (eq this.fields.[System.WorkItemType] 'User Story')}}
- #{{this.id}}
{{/if}}
{{/forEach}}
{{else}}
None
{{/if}}

## Bugfixes
{{#if this.relatedWorkItems}}
{{#forEach this.relatedWorkItems}}
{{#if (eq this.fields.[System.WorkItemType] 'Bug')}}
- #{{this.id}}
{{/if}}
{{/forEach}}
{{else}}
None
{{/if}}

Useful handlebars helpers can be found here.

Import release notes pipeline in Azure DevOps

After the before mentioned files got committed and pushed to the repository, the pipeline needs to be imported in Azure DevOps.

  1. Open the Azure DevOps project in a browser
  2. Navigate to Pipelines
  3. Click New Pipeline button (top right)
  4. Select Azure Repos Git
  5. Select the repository that contains the pipeline (YAML) files
  6. In configure step select Existing Azure Pipelines YAML file
    Note: depending on the content of your repo you may have to click on Show more button to see the option
  7. Select the branch that contains the YAML files (i.e. develop)
  8. The choose the release notes pipeline file as Path
  9. Click Continue
  10. Click on the down arrow right to the Run button and then click Save
  11. [OPTIONAL] Click the three dots on the top right corner and then click Rename/move to rename the pipeline

Authorize build service

To make the release notes pipeline work, the following permissions have to be granted to the build service.

  1. Navigate to the project wiki
  2. Click on the three dots
  3. Select Wiki security
  4. Select the project specific build service
  5. Set Contribute to Allow
  6. Open Project settings (bottom left)
  7. Navigate to Repositories
  8. Select the repository that contains the YAML pipeline
  9. Switch to tab Security
  10. Select the project specific build service
  11. Ensure Contribute is set to Allow for the project specific build service

That’s it, the release notes pipeline is now ready and will run on the next commit to main branch.

Local Testing

To speed up and especially to simplify development of the pipeline task and its templates, there is a tool for local testing provided in the repository of the extension.

It’s pretty straight forward to set up local testing.

  1. Clone the GitHub repository of the Az DevOps extension
  2. Navigate to the cloned repository (~\AzurePipelines\Extensions\XplatGenerateReleaseNotes\V3\testconsole)
  3. Create a handlebars template
  4. Create/adjust settings file
  5. Create a Personal Access Token (PAT) in Azure DevOps
  6. Run the following command
    GenerateReleaseNotesConsoleTester.js --filename build-settings.json --pat AZ_DEVOPS_PAT_HERE

For debugging and troubleshooting purposes, you can dump the whole payload/data to the output file by setting "dumpPayloadToFile": "true" in build-settings.json and by adding {{json propertyToDump}} to your handlebars template.

UPDATE 02.07.2024

If your pipeline run fails with the following error message, check if Protect access to repositories in YAML pipelines setting is enabled (see Project settings > Pipelines > Settings).

ERROR: Repository associated with wiki ID 'WIKI_ID' does not exist or you do not have permissions for the operation you are attempting.
 
##[error]Bash exited with code '1'.

Either disable the setting, if feasible or add resources and uses to the pipeline as stated here.

Useful links

Leave a comment

Website Powered by WordPress.com.

Up ↑