sver: Easy semantic versioning of your artifacts

Sep 7th, 2021

Vlad Iovanov avatar

Vlad Iovanov

Engineering

math equations

As we all know, software versioning is super important. At Aserto, we mostly deal with go binaries and container images. We knew that we wanted to use semver as a strategy from the start, but we couldn’t find a solution with the right features for us. We looked at a few options, including great projects such as svu, git-semver, semantic-release, and goreleaser.

These didn’t work out, because they either prescribe a workflow that we didn’t want to adopt, or the versions they generate are not unique. None of them help with versioning container images.

We ended up creating our own so we could generate semantic versions that are unique, reproducible and sortable for each individual commit. Generated versions don’t collide between different commits.

In a nutshell, sver reads your git repo for a tag, then calculates a version for you. If you’re on a tag, it will print that tag. If you’re not on a tag (you have some commits on top of a tag), it will create a version based on the last commit and the number of commits since the last tag.

We also detect if your repo is dirty so we can add a -dirty pre-release identifier.

You can read more about this in the project’s README.

Installing

If you have Golang installed, you can get sver with the following command:

$ go install github.com/aserto-dev/sver/cmd/sver@latest

For brew users:

$ brew tap aserto-dev/tap
$ brew install aserto-dev/tap/sver

Getting your current version

Let’s play with the sver repository itself. You can clone it from github:

$ git clone git@github.com:aserto-dev/sver.git
$ cd sver

Now, we can run sver and easily obtain the current version:

$ sver
1.3.2

Your current version is interesting, but so is the next. You can calculate it by using the -n flag. You tell it which piece of the semver to bump — patch, minor, major.

$ sver -n patch
1.3.3
$ sver -n minor
1.4.0
$ sver -n major
2.0.0

Docker image tagging

As I alluded to earlier, another thing that sver can do is figure out how you should tag your docker image, which can sometimes be cumbersome to figure out.

When someone consumes an image, they sometimes use latest to pull. But that’s not always best practice. You typically want to be specific about what versions you consume, because APIs change. This is the whole point of semantic versioning.

But you also don’t want to manually babysit upgrades. You likely want to pick a version, and receive bug-fixes for it without worrying too much about it. This means you want to tell your container runtime to pull a version that looks like x.y.* or x.*.

sver helps you tag your images so such a thing becomes easy, and you don’t have to worry about it again.

Continuing with our example of looking at sver itself, we can look at the proposed set of tags for sver's next tagged container release:

$ sver tags -s ghcr.io aserto-dev/sver -u ogazitt -p <PAT>
1.3.2
1.3
1
latest

1.3.2 — your new version

1.3— because it’s the latest in the 1.3.* series

1 — because it’s the latest in the 1.* series

latest — because there’s no other version that’s higher

Now let’s say customer X uses version 0.1.0, and they’ve found a bug. So you prepare a patch, and create tag 0.1.1. When you ask sver what the docker tags for your image should be, it will say:

0.1.1 — your new version

0.1 — because it’s the latest in the 0.1.* series

0 — because it’s the latest in the 0.* series

It’s not outputting latest because 1.3.2 is higher than 0.1.1.

Github action

We also maintain a github action for your convenience. You can use it in your workflows to determine your current version (including image tags) so you can build your artifacts.

You can also automatically calculate a next version if you want to automatically tag your repo when something happens.

Here’s an example of how we use sver in a Github workflow for one of our repositories:

steps:
- uses: actions/checkout@v2
  with:
  fetch-depth: 0
- name: Login to GitHub Packages Docker Registry
  uses: docker/login-action@v1
  with:
    registry: https://ghcr.io
    username: ${{ secrets.DOCKER_USERNAME }}
    password: ${{ secrets.DOCKER_PASSWORD }}
- name: Calculate Tags
  id: "calc_tags"
  uses: aserto-dev/sver-action@v0.0.13
  with:
    docker_image: "aserto-dev/aserto-console-backend"
    docker_registry: "https://ghcr.io"
    docker_username: ${{ secrets.DOCKER_USERNAME }}
    docker_password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build Image
  run: |
    docker build -t "aserto-dev/aserto-console-backend" .
- name: Push image to GitHub Container Registry
  run: |
    echo "${{ steps.calc_tags.outputs.version }}" | \
    xargs -I{} bash -c \
      "docker tag aserto-dev/aserto-console-backend \
      aserto-dev/aserto-console-backend:{} && \
      docker push aserto-dev/aserto-console-backend:{}"

In conclusion…

If you have similar needs to ours when it comes to versioning, we invite you to use sver and perhaps even contribute to make it better!

Vlad Iovanov avatar

Vlad Iovanov

Founding Engineer