Dear package, give git tags to your versions

Every now and then, we at Gemnasium.com go through the source code of some package that’s been reported to be vulnerable. Not only we want to better understand the security issue, we also want to be sure about which versions are affected and which versions are fixed. This can be quite easy when the source is on GitHub, and when each individual version corresponds to a git tag, or very difficult (and frustating) when there’s not relation between the two. While some package managers do it right, others don’t really integrate with VCS. Spoiler: the more recent the package manager is, the better it is regarding this!

Venerable Python

Python certainly deserves some respect as it predates git and the popular VCS we’re using nowadays. So it’s no surprise that Python doesn’t integrate with git tags. To make a new package version available to the users, a Python developer edits setup.py, updates the version number, creates a new distribution, and publishes the “dist” on Pypi.

Actually there are a few tools that makes possible to publish a package version and create a tag on the repo altogether, namely bumpversion, changes and zest.releaser. So there are some options to do it right.

Ruby can’t make it, but it can learn

The build process that results in a Ruby gem is driven by a Gemspec file. Among other things, the Gemspec contains the version number defined a string. “gem build” knows absolutely nothing about VCS like git. No luck here.

A prolific Ruby developer who goes by Postmodern addressed this issue a few years ago and proposed Rake tasks to create git tags automatically. Ruby is a dynamic community, so we can be sure that there are other tools in the wild to do this as well.

Bump, tag with “npm version”

The version of a npm package is stored in a JSON file appropriately named package.json and it contains the version number. As explained in the great getting started video, the developer can either update the version number manually or rely on npm version to do it, prior to publishing with npm publish.

npm version is a good ally as it will create a git tag by default:

If run in a git repo, it will also create a version commit and tag. This behavior is controlled by git-tag-version (see below), and can be disabled on the command line by running npm –no-git-tag-version version. It will fail if the working directory is not clean, unless the –force flag is set.

This is a lot better than Python and Ruby! Still, one can do it wrong and edit the version manually.

Packagist is aware

PHP Composer builds a package out of a composer.json file that’s similar to npm’s package.json. Even though this JSON file used to contain the version number, Packagist.org now provides a better way to manage the versions:

The easiest way to manage versioning is to just omit the version field from the composer.json file. The version numbers will then be parsed from the tag and branch names.

So the developer can focus on the source repo, and Packagist will automatically serve new package versions based on the git tags. Easy.

Bower needs your tags

Bower goes even further: package versions are git tags. There’s just no other way to publish a new version. See the instructions to register a package:

Your package must be publically available at a Git endpoint (e.g., GitHub). Remember to push your Git tags!

This is by design: Bower.io is a lightweight registry that can’t even list the versions of a package.

Go will enter the dance soon

While there is no (official) package manager, nor central registry in go, the use of go get is far from flawless. There is no way to specify a specific version. At best, go users can use gopkg.in to lock to a tag or branch. This is even the official way for some packages like gobrake, the airbrake go client:

go import ( "errors" "gopkg.in/airbrake/gobrake.v2" )

This will install the last commit of the v2 git branch. Only issue with this solution: the namespace of the package is not visible. gopkg.in/airbrake/gobrake.v2 will resolve to https://github.com/airbrake/gobrake/tree/v2.0.2 . Gopkg.in is therefore acting as a registry, with all the security issues implied. The go core team is finally decided to address this issue, and currently working on it. It’s a big step forward, and we are waiting for this issue to be closed to support Go in Gemnasium.

Reducing the gap

The trend is certainly good: new packages have tags on their repository (be it a git repo or not), either because it’s good practice, fully integrated, or even mandatory. That reduces the gap between a package and its source code, and this certainly a good thing when one has to debug.

There’s a huge legacy of packages that don’t stick to that simple rule though. Until these packages are gone, we at Gemnasium will have to navigate through the commits, possibly through the packages, to make sure about what’s fixed and what’s not. This is a tedious task, but the accuracy of the advisories we publish are certainly worth the efforts!