Automate GitHub Releases with CircleCI
Tags: devops, github, circleci, tutorial
Published: 2018-10-01 16:56:01

---
title: Automate GitHub Releases with CircleCI
published: true
tags: devops, github, circleci, tutorial
canonical_url: https://circleci.com/blog/publishing-to-github-releases-via-circleci/ 
---

_This article was originally posted on CircleCI's [blog][1]._

***

Releases is a GitHub feature that allows you to present significant snapshots of your code, marked with a git tag, in GitHub's nice UI. If you're not currently using releases, I want to show you why you might want to, and how to implement them automatically.

With releases, you get what tags provide--a version number and description--but you also get a longer section for release notes and a place to store and display release artifacts. This means your software's binary, .deb, .rpm, and AppImage files will be hosted by GitHub for each release, providing a convenient place for users to install your software.

In this post, I will show you how to create releases within CircleCI. For a more general overview, see [GitHub's doc on creating releases][gh-creating-releases].

At its core, GitHub Releases is simply a GitHub feature layered on top of git tags. Let's break it down:

### Git tags

Git tags give you a version number to mark a specific git commit as well as a short description. Believe it or not, just pushing a git tag to GitHub will create a new release in the "Releases" tab of your project on GitHub. This is a barebones release that includes only the tag information.

### Releases

Releases add to git tags by providing a longer, "rich" description, the ability to mark the tag as a normal release or a pre-release, and most importantly, the ability to upload artifacts such as binaries for that release. Uploading these artifacts from CircleCI is what we're going to walk through here.

## Tools

There are a few ways to publish artifacts from a CircleCI build to a GitHub Release:
- manually with `curl` and the GitHub API
- using [GitHub Release][github-release]
- using [GotHub][gothub] (a GitHub Release fork)
- and using [ghr][ghr], which is what we'll use in our example

### Using ghr

The `ghr` command full details can be [found on GitHub][ghr], however, we really only need to learn one subcommand:

```
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/
```

This command uploads specific artifacts, typically binaries, to our GitHub release on GitHub. Let's break this down:

- `ghr -t ${GITHUB_TOKEN}` - Here we start the command and pass it a GitHub token for authentication. This is a [GitHub Personal Access Token][github-token] and not an OAuth token/key.

Specifically created via your personal GitHub account, you'll want to safeguard this token and only load it into the CircleCI environment via a [private environment variable][private-envars].

- `-u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME}` - This is where we pass the GitHub user/org name and the repository name. Both values are passed via built-in CircleCI Environment Variables. No extra work is needed.

- `-c ${CIRCLE_SHA1}` - Here we provide the Git commit hash (available in CircleCI as `$CIRCLE_SHA1`) to `ghr`. This tells it which commit, and thus tag, the release is for.

- `-delete` - This deletes the git tag and release if it already exists.

- `${VERSION}` - We set the git tag/release version via a `$VERSION` environment variable. This can be any variable you want or a string. Typically this is the same as the version of your software release.

- `./artifacts/` - Then we set the PATH to find the artifacts. In this case, everything in a directory called `artifacts` inside the current directory.

### Plain CircleCI 2.0 example

We've walked through how to use `ghr` to create a GitHub Release but let's see how it can look inside of a CircleCI 2.0 configuration file:

```yaml
  publish-github-release:
    docker:
      - image: circleci/golang:1.8
    steps:
      - attach_workspace:
          at: ./artifacts
      - run:
          name: "Publish Release on GitHub"
          command: |
            go get github.com/tcnksm/ghr
            VERSION=$(my-binary --version)
            ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/
```

### CI Builds CircleCI 2.0 example

This is a CircleCI job called `publish-github-release` that uses a [CircleCI Go convenience image][go-img]. Here's the breakdown:
- Using [workspaces][workspaces], we pull in the binaries for our project from a previous job (not covered in this post).
- This job pulls and compiles `ghr` via `go get` (since `ghr` is written in Go).
- It populates `$VERSION` with the output of `my-binary --version`, our example application for this post.
- We then use the `ghr` command to upload the binaries in the `artifacts` directory to GitHub.

We can shave a few seconds off of the job above by using the [`ghr` Docker image][ghr-img] from [CI Builds][ci-builds]. It's a lightweight Docker image that already has `ghr` installed. Here's how the new example config would look:

```yaml
  publish-github-release:
    docker:
      - image: cibuilds/github:0.10
    steps:
      - attach_workspace:
          at: ./artifacts
      - run:
          name: "Publish Release on GitHub"
          command: |
            VERSION=$(my-binary --version)
            ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/
```

### Workflow example

Here's an example of how one of the variations of the above job can be implemented within a [Workflow][workflows] to publish tagged commits to GitHub releases:

```
workflows:
  version: 2
  main:
    jobs:
      - build:
          filters:
            tags:
              only: /^\d+\.\d+\.\d+$/
      - publish-github-release:
          requires:
            - build
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /^\d+\.\d+\.\d+$/
```

In this example, we have CircleCI kick off the `publish-github-release` job when a git tag is pushed. How exactly you choose when to publish a GitHub release is up to you, but here we'll do it with a SemVer-like git tag. We say SemVer-like because the tag regex we're using `/^\d+\.\d+\.\d+$/` matches tags such as `1.2.3` but doesn't take into account all of the rules of [SemVer][semver] (Semantic Versioning).

For the sake of completeness, here's what a complete regex for SemVer would look like according to [rgxdb.com](https://rgxdb.com/r/40OZ1HN5):

```
/(?<=^[Vv]|^)(?:(?<major>(?:0|[1-9](?:(?:0|[1-9])+)*))[.](?<minor>(?:0|[1-9](?:(?:0|[1-9])+)*))[.](?<patch>(?:0|[1-9](?:(?:0|[1-9])+)*))(?:-(?<prerelease>(?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:0|[1-9](?:(?:0|[1-9])+)*))(?:[.](?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:0|[1-9](?:(?:0|[1-9])+)*)))*))?(?:[+](?<build>(?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:(?:0|[1-9])+))(?:[.](?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:(?:0|[1-9])+)))*))?)$/
```



[gh-creating-releases]: https://help.github.com/articles/creating-releases/
[github-release]: https://github.com/aktau/github-release
[gothub]: https://github.com/itchio/gothub
[ghr]: https://github.com/tcnksm/ghr
[github-token]: https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
[private-envars]: https://circleci.com/docs/2.0/env-vars/#setting-an-environment-variable-in-a-project
[workspaces]: https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
[go-img]: https://circleci.com/docs/2.0/circleci-images/#go-golang
[ghr-img]: https://github.com/cibuilds/github
[ci-builds]: https://github.com/cibuilds
[workflows]: https://circleci.com/docs/2.0/workflows/
[semver]: https://semver.org/
[1]: https://circleci.com/blog/publishing-to-github-releases-via-circleci/