Willy's blog

Automating semver releases with commitizen

SemVer is a great way to version an application. Languages like rust have fully embraced it.

A SemVer version looks like this: 1.2.0

Quite simple, right?

We can map that to MAJOR.MINOR.PATCH where

MAJOR ➡️ BREAKING CHANGES ⚠️

MINOR ➡️ New stuff 🎉

PATCH ➡️ Security 🔒 and bug fixes 🐛

That's the highlight.

Making a new release

Usually, when making a new semantic version (semver), you have to review your commits, check if there's any braking changes, then check if there are new features, otherwise, it's just a patch. This process can be tedious, but semver gives developers a lot of information about a release, like if they can update safely, new features, or they must update.

The release process can be fully automated, but it has a price.

The Price

  • Write parseable commits

Automating release

By writing commits this way, we have to think, in that moment, what kind of change we are introducing. And that information get's encoded in the message.

Let's see a simple rule for parseable, easy to map messages:

Include MAJOR:, MINOR:, PATCH: at the beginning of each commit. If not present the commit will be skipped, and it won't be released.

Commits examples:

MAJOR: Change public interface for class User

MINOR: Add new type of user (employee)

PATCH: Fix full name not being displayed properly

And that's it! The next step is to use a tool to collect the commits, and generate the correct semver.

Commit tips

  • Talk imperative and follow this rule: If applied, this commit will <commit message> 0
  • Keep the subject short

Introducing commitizen

Commitizen is a tool to do exactly that.

I created it in order to automate that process. Based on existing tools from the JS ecosystem but which I found hard to use.

By default it parses the widely popular commit rules: conventional commits.

But you can easily extend commitizen to create the example given before.

Not only it will create the version, but it can also generate the changelog.

It's really easy to use, first create a .cz.toml file in your project's root.

[tool.commitizen]
version = "2.5.1"
version_files = ["setup.py", "Dockerfile", "src/__version__.py", "scripts/publish"]

And that's it, by running a single command we get the version and the changelog.

cz bump --changelog

The veresion_files will also bump the version in the specified files.

CI/CD

diagram of semantic release

In this diagram, you'd execute commitizen during the "merge job" (green in the diagram), where "bump version" is highlighted.

For example, if you are using Github Actions, you'd add this to your job:

on:
  push:
    branches:
      - main

Commitizen then, will generate a new commit, with the updated version in the files and the changelog; and a new tag.

Both are pushed back.

For the new commit, we should not trigger again the CI.

The new tag, instead, should trigger another job, "tag job" (purple in the diagram), which will take care of the release, which can include:

  • deploying to kubernetes
  • publishing to pypi/npm/cargo
  • deploying to a cloud service, like AWS

Done!

Recap

Standards

The Price

  • Write parseable commits

Command

cz bump --changelog

Conclusion

Try commitizen and check the repo!

We aim for simplicity, trying to make this process as simple as possible, but the tool is quite flexible, explore it, and see if it fits for your use cases.

Hey, hello 👋

If you are interested in what I write, follow me on twitter