AsyncAPI versioning in practice

Jonas Lagoni Avatar

Jonas Lagoni

ยท4 min read

Before getting comfortable, this is a continuum of the first versioning post which clarifies the version strategy this post takes into practice.

The workflow at which you propose changes to the APIs is what you can build the version strategy around. So even though I want to focus on the versioning aspect, I don't think you can bring that forward without also talking about how changes are to be proposed and integrated.

The Workflow

I personally enjoy using the workflow process we have at AsyncAPI, and I want to use the same workflow for changes against the APIs for GamingAPI.

The workflow is as follows: Open an issue -> Open a PR -> Merge the changes.

This workflow has a couple of benefits that will be paramount to have down the line:

  1. We can create GitHub workflows to automatically check against the design guidelines.
  2. Once a PR has been merged, it's possible to automate the release process and do different kinds of actions such as version bumping (as the next section focuses on), bundling, automatic code generation, etc.

Versioning in practice

When you open a PR, you should not care about manually changing the version. In an optimal world, the CI system should be able to automatically find the correct version change based on the changes and your version strategy. However, we will have to settle with using something called conventional commits, at least for now. In the future, I might want to leverage AsyncAPI Diff to not rely on conventional commits and human opinion on what changed to determine the new version number.

Back to conventional commits, they enable you to make iterative changes to the APIs. For node projects, you can utilize something like sematic release which makes the release process much easier.

For versioning AsyncAPI documents I could not find any tools to help change the version of the API through conventional commits. Therefore I had to create one, jonaslagoni/gh-action-asyncapi-document-bump.

Because the AsyncAPI documents all reside in one repository, we need to create a GitHub workflow for each AsyncAPI document. For the Rust game server, its AsyncAPI document is bumped through the following GitHub workflow:

1name: Bump release rust server
2env:
3  GH_USER: jonaslagoni
4  GH_EMAIL: <[email protected]>
5on:
6  workflow_dispatch: 
7  push:
8    branches:
9      - main
10    paths:
11      - documents/components/**
12      - documents/rust_server.asyncapi.json
13jobs:
14  bump:
15    runs-on: ubuntu-latest
16    steps:
17      - name: Checkout repository
18        uses: actions/[email protected]
19      - name: Automated Version Bump
20        id: version_bump
21        uses: jonaslagoni/[email protected]
22        env:
23          GITHUB_TOKEN: '${{ secrets.GH_TOKEN }}'
24        with:
25          path-to-asyncapi: ./documents/rust_server.asyncapi.json
26          skip-tag: 'true'
27          skip-commit: 'true'
28          commit-message: 'chore\(release\): rust server v{{version}}'
29      - if: steps.version_bump.outputs.wasBumped == 'true'
30        name: Create Pull Request with bumped version
31        uses: peter-evans/[email protected]
32        with:
33          token: '${{ secrets.GH_TOKEN }}'
34          commit-message: 'chore(release): rust server v${{steps.version_bump.outputs.newVersion}}'
35          committer: '${{env.GH_USER}} ${{env.GH_EMAIL}}'
36          author: '${{env.GH_USER}} ${{env.GH_EMAIL}}'
37          title: 'chore(release): rust server v${{steps.version_bump.outputs.newVersion}}'
38          body: Version bump rust server
39          branch: 'version-bump/rust-server-v${{steps.version_bump.outputs.newVersion}}'

To briefly explain what happens in the workflow.

  1. In the Automated version bump job the GitHub action is figuring out, based on the commit history of relevant files, how it should bump the API version in the AsyncAPI document. It finds all commits up until it encounters the declared release commit message which bumped the version last time (defined in commit-message).
  2. Based on the related commits it bumps the AsyncAPI version accordingly to conventional commits.
  3. If the AsyncAPI document was bumped, the Create pull request with bumped version job commits the changes and creates a PR with the new version for the API.

And that's it, you now use conventional commits to make continuous releases. One important thing with conventional commits is they of course only work when the commit title follows the specification.

Ensure PRs follow conventional commits

To ensure that we don't commit wrong commit titles, we need to make sure each PR title (so when commits are squashed it triggers the correct version change) are linted against the conventional commits specification. For that you can use the simple GitHub action amannn/action-semantic-pull-request:

1name: "Test PR"
2
3on:
4  pull_request_target:
5    types:
6      - opened
7      - edited
8      - synchronize
9
10jobs:
11  test:
12    runs-on: ubuntu-latest
13    steps:
14      - name: Lint PR title
15        uses: amannn/[email protected]
16        env:
17          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Next

Next up is to ensure that each PR comply with all the design guidelines so they are not an empty promise.

Photo by Say Cheeze Studios on Unsplash