Identifying breaking changes in pull request of a Go library with GitHub Action

Backwards compatibility is a core feature of Go. Following Go1 compatibility promise the team does a great job extending and improving the language while allowing developers to focus on their code, rather than on reading upgrade manuals.

Backwards compatibility is one of the key factors of rapid ecosystem and popularity growth. This works for Go, this also works for smaller libraries. Software that "just works" and keeps doing that over time saves a lot of effort.

Go provides a variety of tools to reduce or eliminate breaking changes. Some practices are described in Codebase Refactoring (with help from Go) . When breaking changes seem unavoidable Go Modules with semantic import versioning allow smooth transition.

How can a developer identify if the changes are breaking compatibility?

Breaking changes can be divided in two groups:

  • changes that fail compilation of existing code,
  • changes that pass compilation, but exhibit unexpected behavior.

Changes from the second group are harder to identify and usually it takes a comprehensive test suite and/or thorough code review to decide.

Changes from the first group are easier to find with automated tools. Recently Go team made an experimental tool gorelease that can detect some of the incompatible changes.

This tool is very helpful, as it is easy to lose manual track of compatibility, especially when working with bigger changes.

To make this tool even more helpful, I made a small GitHub Action script that runs gorelease for pull requests and comments the report.

Such comment makes it easy to recognize unexpected breaking changes and take action to improve the code.

.github/workflows/gorelease.yml

# This script is provided by github.com/bool64/dev.
name: gorelease
on:
  pull_request:
env:
  GO111MODULE: "on"
jobs:
  gorelease:
    strategy:
      matrix:
        go-version: [ 1.16.x ]
    runs-on: ubuntu-latest
    steps:
      - name: Install Go
        uses: actions/setup-go@v2
        with:
          go-version: ${{ matrix.go-version }}
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Gorelease cache
        uses: actions/cache@v2
        with:
          path: |
            ~/go/bin/gorelease
          key: ${{ runner.os }}-gorelease
      - name: Gorelease
        id: gorelease
        run: |
          test -e ~/go/bin/gorelease || go install golang.org/x/exp/cmd/gorelease@latest
          OUTPUT=$(gorelease || exit 0)
          OUTPUT="${OUTPUT//'%'/'%25'}"
          OUTPUT="${OUTPUT//$'\n'/'%0A'}"
          OUTPUT="${OUTPUT//$'\r'/'%0D'}"
          echo "::set-output name=report::$OUTPUT"
      - name: Comment Report
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          header: gorelease
          message: |
            ### Exported API Changes Report

            <pre>
            ${{ steps.gorelease.outputs.report }}
            </pre>

This script is also available as a part of 🛠️ Go development helpers.

46