4 min read

How to automate your release notes

Two facts are generally true for software releases.  One, as users, we wish software releases came with better release notes, and two, we hate creating release notes when it’s our turn to ship software updates. Creating release notes requires someone to go back in time to the last release, gathering the relevant information, and compiling it into a document to share with users. This can be quite a challenge and very time consuming. Wouldn’t it be nice to automate this process?

In this blog post, I’ll walk you through an application I created to automate release note generation for source code hosted in GitHub using Azure Functions. This walk-through highlights the services used for the application and comes with a sample repo so that you can create your own generator and explore ways to enhance your workflow.

Azure Functions and GitHub webhooks make it easy to merge your GitHub workflow with serverless solutions. This inspired the creation of the GitHub Release Notes Generator, an application written in C# that creates neatly formatted release notes, using GitHub, Azure Functions, and Azure Storage, to shave time off of your release process and ensure all relevant information reaches your team and users.

Rendered markdown file of example release notes

Serverless applications are event-driven, so something needs to happen to make your code run, and one of the benefits of this is that you’re only paying for the time your application runs. This makes Azure Functions a good fit for the generator. Functions are hosted within a function app, which provides a central area for configuring general settings and key management for all functions. When a new release is created in the selected GitHub repository, a GitHub webhook function executes code to create the release notes.

This results in writing less code: configuration for all functions happens in one place, and the functions themselves have bindings, which allow your functions to interact with additional Azure services or external ones, such as the GitHub webhook. After creating the function app and function, there is very minimal setup to make sure the webhook is set up for the proper GitHub event and repository.

Markdown files in a blob container

The function now can be updated to generate the notes. Since GitHub uses Markdown for documentation in repositories and releases, the generator also uses markdown and creates a markdown file with relevant release information. Azure blob storage provides a managed solution for unstructured data, such as markdown files of varying sizes and will scale as more releases are created. The end result is a file store available for distributed access or serving to a browser. Using the Azure Storage API, the generator creates the markdown file, then appends additional information about the release. With a storage account you can create a blob container, where the release notes will live.

When a release is created, its specific details can be found in the webhook’s HTTP response. The response includes information about the event and additional metadata about the repository, which is where the generator sets the file name as the release name and header. It also appends the release’s body to the file.

Here I extract the name and body of the new release from the webhook response and set the release name as the file name:

public static async Task Run(HttpRequestMessage req, TraceWriter log)
    // Get request body
    dynamic data = await req.Content.ReadAsAsync<object>();

    // Extract github release data from request body
    string releaseBody = data?.release?.body;
    string releaseName = data?.release?.name;

    //Connect to storage account
   CloudStorageAccount storageAccount = CloudStorageAccount.Parse(Environment.GetEnvironmentVariable("StorageAccountConnectionString"));
    var blobClient = storageAccount.CreateCloudBlobClient();
    var container = blobClient.GetContainerReference("releases");

    //Set name of file
    var blob = container.GetBlockBlobReference(releaseName + ".md" );


It’s not possible to include all of the Issues and Pull Request history with this event alone, so we’ll need to get some additional information about the repository through the Octokit.NET API. This requires some minimal setup in the GitHub website by creating a new OAuth app, then using the app’s name to access the API. The function uses the API with a date qualifier to get the last two weeks’ closed Issues and merged Pull Requests.

Here I get issues and pull requests from GitHub through the Octokit.NET API:

public static async Task<string> GetReleaseDetails(IssueTypeQualifier type)
    //Connect to client with OAuth App
    var github = new GitHubClient(new ProductHeaderValue(Environment.GetEnvironmentVariable("ReleaseNotes")));
    var twoWeeks = DateTime.Now.Subtract(TimeSpan.FromDays(14));
    var request = new SearchIssuesRequest();

    //Find Issues or PRs closed within the past 14 days in specified Repo 
    request.Type = type;
    request.Closed = new DateRange(twoWeeks, SearchQualifierOperator.GreaterThan);

Azure Functions are a quick and straightforward way to create your own customizable serverless workflows. Working with external services, such as GitHub, requires minimal setup with webhooks and makes getting started fast and manageable. The release notes generator is just one of several possible tools to do this, and its sample code is a good start if you’re interested in exploring possibilities that work for you.

The sample includes instructions for building your own generator from scratch or deploying it from your Azure subscription. Once you’ve got your own notes generator up and running, be sure to visit the docs and samples to see what else you can do with serverless applications.

Sample code

GitHub Release Notes Generator

Additional resources

An introduction to Azure Functions

Create a function triggered by a GitHub webhook

Serverless apps: Architecture, patterns and Azure implementation (e-book)

Azure Storage Documentation

Feedback or questions? Let us know in the comments below.