DEV Community


A sensible semantic versioning process for Docker images

johnnystarr profile image Johnny Starr ・3 min read

I'm a big fan of Ruby. I've literally used it almost every day since 2008 when I picked up the "hummingbird" book (v1.8.7). Naturally, I am likely to have a Rakefile at the root of all my repositories. GNU Make has its place, but Rake is always the clear winner in my book.

I've noticed that CI/CD pipelines for Docker images can be challenging for newcomers, so I wanted to share a best practice for bumping versions in a codified manner. And no, I don't mean the goto VERSION text file. This method works of course, but if you want something more sophisticated read on.

To get started, you'll need Ruby installed and create the following two files:

  • .version.yml
  • Rakefile

In our .version.yml file, let's add some content

version: 0.0.1
promote: major
phase:         # optional

Enter fullscreen mode Exit fullscreen mode

Our intention here is to cook up an initial release version 1.0.0. The value we're passing for the version doesn't really matter, as long as its below major version 1. The promote: key is going to determine the semantic bump that we plan to use. Now let's create our Rakefile:

require 'semantic'
require 'yaml'

def next_version
  # load the current version
  version_yml = YAML.safe_load('.version.yml'))
  current     = version_yml['version']
  level       = version_yml['level'].to_sym
  phase       = version_yml['phase']

  # create a forecast of what our future version will be
  version = current
  version = version.increment!(level)

task :bump do
  sh "docker tag myproject:#{next_version}" # => myproject:1.0.0

Enter fullscreen mode Exit fullscreen mode

So what exactly is happening here? We've defined a method in the #Kernel namespace that we can employ throughout our Rake tasks. It reads the YAML file and provides the appropriate bump to our version. The :bump task simple runs the docker tag command, but could be used in conjunction with any other release mechanisms.

Putting it to Work

In order for this to be useful to you and your team, you will need to discuss your release strategy. Ideally, you should only bump your semantic versions on merges to master. Depending on your CI/CD process, you would only run the :bump task during the release phase.


Rake is more than it appears at first glance. It's not simply a Makefile with some Ruby features. It is Ruby; or rather a Ruby environment. By putting Rake to good use, you avoid mangled Makefiles with pseudo-bash scripting baked in.

I also view this as a great mechanism for peer reviews / pull requests. It allows the team visibility on what you're planning to release and why your chosen promotion level is appropriate (or inappropriate). Simply dropping off a number in VERSION doesn't provide the same stimulus in my experience.

The YAML File

Why YAML and not JSON or something else? Meh. I say do what you want! I chose YAML because its human readable and ubiquitous in the DevOps realm lately. You can also add additional fields at any time without breaking functionality. In the above example I left phase: blank. This is great for any modifiers such as pre-release, alpha, beta, rc1, etc.


Unlike the yaml gem, you will need to install semantic yourself. Either use bundler or simply run the following command:

$ gem install semantic
Enter fullscreen mode Exit fullscreen mode

Additionally, this gem (and of course Ruby) will have to be installed on your CI/CD agent(s). I would suggest using Bundler with a Gemfile to accomplish this. If you're concerned about keeping your agents pristine, read up on installing the gems locally to the vendor/ folder.

Final Thoughts

The DevOps frontier is still very "wild west". I'm sure someone will invent a tool that handles versioning in a more succinct way, but what would the fun be in that?

Discussion (0)

Forem Open with the Forem app