Goals: \

  1. Create an automated workflow around the release process
  2. This automated flow should handle both releases and non releases
  3. This flow should not just build for the sake of building

Features:

  1. This workflow should increment versions based on Conventional Commits
  2. The tags should follow Semantic Versioning (semVer)
  3. Releases should contain a Changelog

Now the rundown and the story……
I tried many different ways to get this to work. The primary issue is that Github’s documentation is thorough but confusing. Luckily I have many resources that I check with to help dig up nuggets. Shout-out here to my friends and colleges in the HangOps Community’s #python channel.

I’ll admit that part of what I have at time of writing is not perfect as it triggers on ANY push to main which is excessive in the sense that it burns Action minutes when possibly not needed. BUT I don’t exactly mind that it runs on every push as with proper branch protection all code changes will be through Pull Requests (PRs) which would be running some workflows anyway.

“Pre-Release” AKA Incoming Code

To ensure All features, I have a lint that runs on every PR. This workflow file does code linting and commit message linting. This makes sure the code “looks” good and also makes sure that all commits in a PR will become changelog list items and can trigger builds/releases when code changes.

Pushes to Main: To release or not to Release

Next we make sure that we cover the actual release process. Here’s where some of the more tricky bits come in. I want to only release on fix,feat, and major corresponding to the Semantic Versioning standard. I also want to not get notified when its not going to create a new tag/release. The way to do this is with a few good if statements. The way I handled this was with some great actions.

  1. Generate the changelog with this action
  2. Check to see if there have been any commits that would justify a new version using IETF-Tools SemVer Action. This also exits silently if no version is needed and returns a value of next = Null. That output is the key to my workflow.
  3. Create Release: This uses Softprops action-gh-release This creates the release based on the tag generated in step 2 and includes the changelog generated. HOWEVER, if that step returns new = Null this step gets skipped.

Here comes the neat part. This was created with help from the aforementioned HanOps community and a link to this StackOverflow post answer

There is a feature where one workflow will trigger another which gets used because a workflow cannot be triggered on an action done by another workflow (EG Workflow 1 creating a release will not trigger Workflow 2 which only has triggers for when a new release is created) This is where workflow chaining comes in.

Triggering another workflow is easy. Changing when that workflow is triggered is a bit obscure. The trick comes from using Job Outputs from the first job (The release generating job) to act as the gate for the second job as an if statement.

The file can be directly accessed here release.yml Below is an abridged version that covers the essentials.

jobs:
    Release Generator:
        outputs:
            output1: ${{ steps.semver.outputs.next }}
        steps:
            < Some other steps here>
            
            - name: Get Next Version
              id: semver
              uses: ietf-tools/semver-action@v1
              with:
                token: ${{ github.token }}
                branch: main
                noVersionBumpBehavior: silent
            < Do more things>

    trigger_build:
        needs: Release
        if: ${{ needs.Release.outputs.output1 != 0 }}
        uses: ./.github/workflows/build_packages.yml