Automate changelog and releases creation in GitHub
Tags: changelog, github, releases, actions
Published: 2020-09-25 13:32:56

---
title: Automate changelog and releases creation in GitHub
date: 2020-09-23 00:00:00 UTC
published: true
tags: changelog,github,releases,actions
canonical_url: https://bitsofknowledge.net/2020/09/23/automate-changelog-and-releases-creation-in-github/
---

Keeping up to date the Changelog and generating GitHub releases is one of those tasks I always think it is important to do, but I feel it becomes a chore the more you have to do it manually on a given project. In one of my recent OSS projects, [camper](https://github.com/renehernandez/camper), I decided from the beginning that I didn't want to be manually generating the Changelog and the GitHub releases information.

After researching different options, I landed on [github-changelog-generator](https://github.com/github-changelog-generator/github-changelog-generator). A neat project, it is a Ruby gem that allows you to automatically generate a changelog based on **tags** , **issues** and **merged pull requests**.

## Implementation [#](#implementation)

Since the project is hosted on GitHub, I went with [GitHub Actions](https://github.com/features/actions) for the implementation as part of the CI process. It was an opportunity to put Actions in practice and get familiar with it. This post is not about an introduction to GitHub Actions, check instead the [Actions Docs](https://docs.github.com/en/actions) for getting started and diving deep into the subject.

Already back!! Great, let's go into the details.

First let's discuss the main requirements that I had in mind:

When committing to `main` branch:

- A new updated Changelog should be generated and committed to `main`. This would account for merged PRs, as well as any direct commit to `main`.
- All latest changes that are not already part of the tagged released, should be grouped under an **Unreleased** section at the top of the Changelog

When pushing a new tag:

- Update the Changelog moving all the unreleased changes under the new tag.
- Create a new [GitHub release](https://docs.github.com/en/github/administering-a-repository/about-releases) containing all the information associated with the latest Changelog tagged entry.

### CI - Changelog workflow [#](#ci---changelog-workflow)

As I explained in the previous section, the Changelog update process consists of two parts. One when merging to `main` and the other when pushing a new tag. The _CI - Changelog_ workflow, as shown below, fulfills the requirement of updating the **Changelog** on every push to the `main` branch. You can find the most up to date version at [here](https://github.com/renehernandez/camper/blob/main/.github/workflows/ci_changelog.yml)

```
name: CI - Changelog

on:
  push:
    branches: [main]

jobs:
  changelog_prerelease:
    name: Update Changelog For Prerelease
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
        with:
          ref: main
      - name: Update Changelog
        uses: heinrichreimer/github-changelog-generator-action@v2.1.1
        with:
          token: $
          issues: true
          issuesWoLabels: true
          pullRequests: true
          prWoLabels: true
          unreleased: true
          addSections: '{"documentation":{"prefix":" **Documentation:**","labels":["documentation"]}}'
      - uses: stefanzweifel/git-auto-commit-action@v4
        with:
          commit_message: Update Changelog for PR
          file_pattern: CHANGELOG.md

```

It works as follows:

1. It checks out the code using the [action/checkout](https://github.com/actions/checkout) v2
2. It proceeds to generate an update for the Changelog by using the [heinrichreimer/github-changelog-generator-action](https://github.com/heinrichreimer/action-github-changelog-generator) action with the following customizations:
  - All closed issues should be part of the Changelog, including those without labels (`issues: true` and `issuesWoLabels: true`)
  - All pull requests should be part of the Changelog, including those without labels (`pullRequests: true` and `prWoLabels: true`)
  - It should group all latest changes under an **Unreleased** section (`unreleased: true`)
  - It adds a new _Documentation_ section to group issues and pull requests with the `documentation` label
3. Then it commits the modified Changelog file back to main using the [stefanzweifel/git-auto-commit-action](https://github.com/stefanzweifel/git-auto-commit-action) action

### Release workflow [#](#release-workflow)

The _Release_ workflow is a more complex pipeline since it not only updates the _Changelog_, but also handles the publishing of a new gem version as well as a new GitHub release associated with the tag being pushed. For this post, we are only focusing on the Changelog and GitHub release related jobs. If you are interested, check the full workflow [here](https://github.com/renehernandez/camper/blob/main/.github/workflows/release.yml)

```
name: Release

on:
  push:
    tags:
      - v*

jobs:
# Other jobs
# ...
  changelog:
    name: Update Changelog
    runs-on: ubuntu-latest
    steps:
      - name: Get version from tag
        env:
          GITHUB_REF: $
        run: |
          export CURRENT_VERSION=${GITHUB_TAG/refs\/tags\/v/}
          echo "::set-env name=CURRENT_VERSION::$CURRENT_VERSION"
      - name: Checkout code
        uses: actions/checkout@v2
        with:
          ref: main
      - name: Update Changelog
        uses: heinrichreimer/github-changelog-generator-action@v2.1.1
        with:
          token: $
          issues: true
          issuesWoLabels: true
          pullRequests: true
          prWoLabels: true
          addSections: '{"documentation":{"prefix":" **Documentation:**","labels":["documentation"]}}'
      - uses: stefanzweifel/git-auto-commit-action@v4
        with:
          commit_message: Update Changelog for tag $
          file_pattern: CHANGELOG.md

  release_notes:
    name: Create Release Notes
    runs-on: ubuntu-latest
    needs: changelog
    steps:
      - name: Get version from tag
        env:
          GITHUB_REF: $
        run: |
          export CURRENT_VERSION=${GITHUB_TAG/refs\/tags\/v/}
          echo "::set-env name=CURRENT_VERSION::$CURRENT_VERSION"

      - name: Checkout code
        uses: actions/checkout@v2
        with:
          ref: main

      - name: Get Changelog Entry
        id: changelog_reader
        uses: mindsers/changelog-reader-action@v1
        with:
          version: $
          path: ./CHANGELOG.md

      - name: Create Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: $ # This token is provided by Actions, you do not need to create your own token
        with:
          tag_name: $
          release_name: Release $
          body: $
          draft: false
          prerelease: false

```

The first job, **Update Changelog** , is almost the same as the one described on the previous section.The difference is that we are generating **released** versions only and thus, there is no `unreleased: true` entry.

The second , **Create Release Notes** , works as follows:

1. It relies on the updated Changelog, thus the presence of `needs: changelog` to force it to wait for the previous `changelog` job completion.
2. Using the [mindsers/changelog-reader-action](https://github.com/mindsers/changelog-reader-action), it proceeds to select the changelog entry associated with the tag being pushed.
3. Using the [actions/create-release](https://github.com/actions/create-release), it generates the GitHub Release using the content of the changelog entry extracted on the previous step

### GitHub action gotchas [#](#github-action-gotchas)

The changelog generator action has some gotchas that are not easy to spot:

- The majority of options specified in the _Update Changelog_ step, such as `issues: true` and `pullRequests: true` default to `true` on the underlying `github-changelog-generator` gem, but are required as part of the action, otherwise they get set to `false`. That tripped me over for a while, until I read the action's implementation, specifically the [entrypoint.sh](https://github.com/heinrichreimer/action-github-changelog-generator/blob/master/entrypoint.sh#L39)
- Adding a new section using the `addSections` field fails if you specify a prefix with multiple words (e.g, _Documentations updates_ as the [changelog generator wiki](https://github.com/github-changelog-generator/github-changelog-generator/wiki/Advanced-change-log-generation-examples) suggests). The issue is with word splitting on the `entrypoint.sh` as discussed in [issue#3](https://github.com/heinrichreimer/action-github-changelog-generator/issues/3).

### Changelog generator limitation [#](#changelog-generator-limitation)

While iterating on the output produced by the `changelog-generator` gem, I realized that I was getting double entries between PRs that are linked to issues (i.e. PRs that close issues when merged). I dug in the documentation trying to find a way of just showing either the issue or PR to no avail. Then I posted an [issue](https://github.com/github-changelog-generator/github-changelog-generator/issues/890) on the github repo and confirmed my suspictions that it is not currently a way to this, due to limitations on the GitHub REST API.

## Conclusions [#](#conclusions)

In this post, we discussed how to automate Changelog and GitHub Releases creation. We went over the details of each of the workflows and describe the steps for each job involved in the workflows. We also mentioned some limitations and gotchas for the github actions and the `github-changelog-generator` gem.

To conclude, thanks you so much for reading this post. Hope you enjoyed reading it as much as I did writing it. See you soon and stay tuned for more!!