rss twitter gitlab github linkedin linkedin
versions release: v0.1.0 adds LICENSE support
Jun 22, 2020

versions was officially announced to the world last month, and since then I’ve been working on adding features I think will make this tool more useful.

Today I’m introducing a new minor version: v0.1.0, but before that…

What did change?

Architecturally versions is pretty straightforward, it consists in the following:

  1. Receive an input of go.mod files, which represent “projects” or “repositories”,
  2. Read and parse those go.mod files with the goal of generating a matrix of packages,
  3. Categorize the packages found in each go.mod file,
  4. Determine a way to indicate which packages have the same and/or different versions,
  5. Print out the results.

v0.0.1 was built with those steps in mind. The initial goal was to quickly implement a proof of concept, an actionable prototype that worked with one objective: to render flavored markdown. This objective was accomplished.

However, this code was not in a shape to be easily extented, sure it did the job, but it required more thought if the plan was to move forward and continue with the development. With that in mind my next goals before making a new release were two:

  1. Clean up and refactor the code to make it easier to read and extend, in order to support rendering multiple outputs, flavored markdown being the first one.
  2. Add a new feature that could be used by any rendering output, this is to make sure the new refactored code was developer friendly.

Cleaning up and refactoring

It was clear that separating the actual parsing from the rendering (and its concrete rules) was needed. The first version assumed flavored markdown was the only rendering output supported, it included concrete rendering rules applicable to this type and it was intertwined with other non-rendering logic. This change was required for allowing adding more ouputs in the near future, like JSON.

In v0.0.1 really everything was implemented in two functions:

  1. versions.NewGoMods for doing something with the parsed go.mod values, and
  2. versions.PrintMarkdown for printing out the actual markdown code.

So, how we go from that really concrete implementation to something more abstract?

The way I thought about it in v0.1.0 was to define the final results in a new Versions type:

// Versions contains the parsed go.mod files.
Versions struct {
	Modules    map[ModuleName]Module
	GoVersions GoVersions
	Packages   Packages
}

The fields in this type contain the parsed values organized in threee different ways:

  • Specifically what each go.mod file contains, Modules field, this includes the Modules:
  • All packages being used, Packages field, organized internally by ModuleName,
  • All Go Versions used, GoVersions field.

Having those three fields available are the basic layer required for allowing different renderers to generate what is needed. markdown is the concrete example so far, this package only renders the results, it still supports the options that were available before but now they don’t affect the parsing process.

Adding a new feature

After cleaning up the code and refactoring it, the second goal was to add a new feature. I thought that adding LICENSE support would be a good excercise, because:

  • It touches a basic type: Package, because licenses are per package and could change depending on the concrete version used,
  • It adds (hypothetically) new dependencies, and
  • It could affect how rendering outputs are implemented.

Therefore adding a feature like this will allow me, the user of the versions package, to determine how good or bad the user experience was (from the development perspective that is), to corroborate whether the final refactored code is better or not.

In the end this was an interesting excercise that proved that the code was easier to work with and also led to another nice change, the introduction of olekukonko/tablewriter for rendering markdown code in a more user-readable way.

Final thoughts

This time I wanted to diverge a tiny bit from the usual announcement-like post and instead walk you through my thought process and how the code evolved. For sure versions is still in its infancy but I think it makes sense to describe the behind-the-scenes from time to time.

To install the current version you can run:

GO111MODULE=on go get github.com/MarioCarrion/versions/cmd/versions@v0.1.0

Using it is the same as before:

versions <full path to 1 go.mod> <full path to 2 go.mod> <full path to N go.mod>

Have fun!


Back to posts