Managing Changelogs with Chag

Open source projects often include some kind of changelog file that helps consumers of the project to be aware of any important changes that have been made between versions. The format and filename of a changelog typically varies from project to project; however, there’s some promising news… http://keepachangelog.com hopes to standardize how open source projects represent changelog files. I’ve recently begun modifying the changelog files of all of my projects to conform to this new changelog standard.

I often need to re-publish the contents of a changelog entry to various other mediums to help keep users of my projects up to date. For example, I often use GitHub Releases to notify users of a new release via email, provide users an atom feed for updates to a project, and provide historical builds of each release. One of the nice things about keepachangelog.com is that it makes it easier to parse changelog files.

Chag

I created chag, short for “changelog tag”, in order to automate the process of extracting changelog information from a project’s changelog file.

Installing chag is simple:

curl -s https://raw.githubusercontent.com/mtdowling/chag/master/install.sh | bash

Chag has several features that helps to automate various aspects of a managing a changelog file.

chag contents

Extract the contents of a changelog entry (either the latest entry or a specific entry by version).

Usage: chag contents [--help] [--file <path>] [--tag <tag>]

Outputs the contents of a changelog entry from a changelog file. If no
--tag option is provided, then the top-most entry in the changelog is
parsed.

Options:
--file     Path to changelog. Defaults to CHANGELOG.md
--tag      Tag version string to parse. Defaults to the latest.
--help     Displays this message.

chag entries

Get a list of versions available in a changelog file.

Usage: chag entries [--help] [--file <path>]

Lists all of the version numbers in a changelog file, separated by new lines.

Options:
--file    Path to changelog. Defaults to CHANGELOG.md
--help    Displays this message.

chag latest

Get the latest version in a changelog file.

Usage: chag latest [--help] [--file <path>]

Get the latest changelog entry version from a CHANGELOG.

Options:
--file    Path to changelog. Defaults to CHANGELOG.md
--help    Displays this message.

chag update

Update a WIP changelog heading to a new version number and date.

Usage: chag contents [--help] [--file <path>] [--tag <tag>]

Outputs the contents of a changelog entry from a changelog file. If no
--tag option is provided, then the top-most entry in the changelog is
parsed.

Options:
--file     Path to changelog. Defaults to CHANGELOG.md
--tag      Tag version string to parse. Defaults to the latest.
--help     Displays this message.

This command allows you maintain an ## Unreleased changelog entry and append content to the entry as you develop a new version. When the new version is ready to tag, you simply call chag update X.Y.Z, where X.Y.Z is the version that you will use when the next release is tagged. Chag will modify the WIP changelog entry to use the new version and will add today’s date to the entry.

chag tag

Create an annotated Git tag using the latest changelog entry.

Usage: chag tag [--help] [--file <path>] [--addv] [-s|--sign] [-f|--force]

Parses a changelog entry for the given tag and creates an annotated git
tag based on the changelog entry.

Options:
--file      Path to changelog. Defaults to CHANGELOG.md
--addv      Pass to prepend a "v" to the git tag (e.g., "v2.0.1")
--sign|-s   Make a GPG-signed tag, using the default git e-mail address key.
--force|-f  Delete an existing tag if present.
--help      Displays this message.

GitHub Releases

GitHub Releases provides a really nice way to release open source software hosted on GitHub. Releases allows your users to list the available versions of a project, download builds, of specific versions, and subscribe to an atom feed that contains release information.

For example, the releases for chag can be found at https://github.com/mtdowling/chag/releases. Chag’s release atom feed can be found at https://github.com/mtdowling/chag/releases.atom. In fact, every project on GitHub provides a release atom feed. Simply substitute mtdowling/chag in the above URL with a project owner and repo name: https://github.com/{owner}/{repo}/releases.atom. Included in each atom feed entry is changelog information– information that can be extracted using chag.

Each time a release is published, any developer that is “watching” the repo will receive an email that lets them know about the release. Included in this email is a description of the changes made in the version– again, information that can be extracted with chag.

GitHub releases can be created using the GitHub API or using a Travis CI deployment provider.

Travis Releases

Travis CI is a continuous integration service used to build and test projects. Most open source projects utilize Travis, and if they don’t, they should. One of the many features offered by Travis CI is the ability to deploy your project after a successful build. Travis CI offers various deployment providers, one of which is GitHub Releases.

I use GitHub releases in several of my open source projects (for example, Guzzle). Utilizing Travis CI to deploy a release means that all I need to do to cut a release is push a tag to GitHub. Travis then sees the new tag, builds my assets, and makes an API call to GitHub Releases to create a new release with the built assets. All you need to do to utilize GitHub releases in your project is modify your .travis.yml file. This can be done rather easily using the travis command line tools.

Configure Travis

First you need to install the Travis CLI:

gem install travis

Next use the Travis CLI to interactively configure your project for GitHub releases.

travis setup releases

Note: You should configure the deploy provider to only deploy on new tags. There is also currently a bug in Travis that requires you to specify all_branches as true in order to build only on tags. See: https://github.com/travis-ci/travis-ci/issues/1675#issuecomment-37851765

Here’s what the deploy section of a configured project’s .travis.yml might look like:

language: bash

before_install:
- do some install commands...

script:
- script used to run your tests...

deploy:
  provider: releases
  api_key:
    secure: long-encrypted-token
  file: chag
  on:
    repo: mtdowling/chag
    tags: true
    all_branches: true

Publish a Release

Now that Travis is configured, any time you push a new tag to GitHub, Travis will create a new GitHub Release. You’ll need to use chag in order for your changelog information to show up in your releases. Chag uses annotated Git tags that contains the latest entry in your changelog as the tag annotation.

Given the following changelog file:

# CHANGELOG

## 1.0.0 - 2014-10-26

Bugfix release

- Addressed issue #12.
- Fixed a bug.

You can tag the 1.0.0 release using the corresponding changelog contents using chag:

chag tag

This command will parse the contents of the latest changelog entry and create a new annotated Git tag that uses the contents as the tag annotation. The command identifies the version to tag as 1.0.0 and extracts the changelog entry contents from the file so that it is used in the tag annotation. This annotated tag is then used by Travis when pushing the release to GitHub releases.

Note: GitHub currently uses the first line of a changelog entry as part of the title in a release.

Thanks

I hope that you find chag useful when managing your changelog files. If you’re not managing a changelog for your project, then I hope that you start, and I hope that you use the format described by http://keepachangelog.com.

P.S.: Chag uses a uses a tool called Bats for automated testing. It made testing a semi-complicated Bash script as painless as possible. If you’re developing a Bash script, then I highly recommend it.