gsemver is a command line tool developed in Go (Golang) that uses git commit convention to automate the generation of your next version compliant with semver 2.0.0 spec.
Why yet another git version tool ?
When you try to implement DevOps pipeline for applications and libraries from different horizons (java, go, javascript, etc.), you always need to deal with versions from the moment you want to release your application/library to the deployment in production.
As DevOps is all about automation, you need a way to automate the generation of your next version.
Then, you have 2 choices:
- you can use no human meaningful information:
- forever increment a number
- use git commit hash
- use build number injected by your CI server
- etc.
- you can use a human meaningful convention such as semver.
The first option is easy and does not required any tool.
However some tools/tech require you to use a semver compatible format version (eg. go modules, helm, etc.). You can still decide to always bump the major, minor or patch number but then your version is not meaningful in you are just doing a hack to be compliant with the spec format but not with spec semantic.
So for the second option, in order to provide human meaningful information by following the spec semantic, you need to rely on some conventions.
You can find some git convention such as:
- conventional commits: generalization of angular commit convention to other projects
- angular commit convention
- gitflow
Then I looked for existing tools and here is a non exhaustive list of what I've found so far:
- GitVersion: tool written in .NET.
- semantic-release: tool for npm
- standard-version: tool for npm
- jgitver: CLI running on java, maven and gradle plugins.
- hartym/git-semver: git plugin written in python.
- markchalloner/git-semver: another git plugin written in bash
- semver-maven-plugin
All these tools have at least one of these problems:
- They rely on a runtime environment (nodejs, python, java). But what if I want to build an application on another runtime ? On a VM, this is probably not a big deal but in a container where we try keep them as small as possible, this can be annoying.
- They are not designed to automatically generate a new version based on a convention. Instead, you have to specify what number you want to bump (major, minor, patch) and/or what type of version you want to generate (alpha, beta, build, etc.)
- They manage the full release lifecycle and so they are tightly coupled to some build tools like
npm
,maven
orgradle
.
I've found some libraries written in go but they don't deal with git commits/tags convention:
I needed a tool to generate the next release semver compatible version based on previous git tag that I could use on every type of application/library and so that is not relying on a specific runtime environment.
That's why I decided to build this tool using go with inspirations and credits from the tools I've found.
Thank you all for the inspirations!
I'd like also to thanks 2 projects that are used in combination with gsemver to better automate the release of this tool:
- conventional commits a commit convention I've decided to adopt in all my commits.
- git-chglog is a customizable CHANGELOG generator implemented in go based on commits log.
- GoGeleaser is a release automation tool for Go projects.
With these 3 tools and gsemver
, it gets easier to automate the release your projects.
Please install gsemver
in a way that matches your environment.
go install github.com/arnaud-deprez/gsemver@latest
For a manual installation, you can download binary from release page and place it in directory registered in your $PATH
environment variable.
You can check with the following command whether the gsemver
command was included in a valid $PATH
.
$ gsemver version
# output the gsemver version
Most of CI server uses - by default - shallow git clone when cloning your git repository.
When performing such a clone, the local copy of your git repository will contain a truncated history and most probably will be detached from HEAD.
As gsemver
is currently using git describe
to compute the next version, it means you should use annotated tag instead of lightweight tag to release your code (see lightweight vs annotated tag).
Likewise, it also needs to have access to at least to the last parent annotated tag.
For these reasons, gsemver
will execute git fetch --tags
before computing the next version.
As gsemver
also needs to know the current branch and it tries to retrieve it with git symbolic-ref HEAD
command.
However most of CI server execute the build in detached from HEAD state and then it becomes hard in git to retrieve the branch from where the build has been triggered.
Fortunately, most of CI server injects the branch name in an environment variable.
That's why gsemver
allows you to use the GIT_BRANCH
environment variable as a backup solution.
gsemver bump
This will use the git commits convention to generate the next version.
The only current supported convention is conventional commits.
It also uses by default main
, master
and release/*
branches by default as release branches and it generates version with build metadata for any branch that does not match.
This is a current limitation but the roadmap is to make more configurable.
The conventional commits integration tests shows you in depth how version is generated. For a more comprehension view, here an example of the logs graph these tests generate:
* 34385d9 (HEAD -> main, tag: v1.2.2) Merge from feature/merge2-release-1.1.x
|\
| * b884197 Merge from release/1.1.x
| |\
|/ /
| * 869c83f (tag: v1.1.3, release/1.1.x) Merge from fix/fix-3
| |\
| | * 22eabaf fix: my bug fix 3 on release/1.1.x
| |/
* | 704fde4 (tag: v1.2.1) Merge from feature/merge-release-1.1.x
|\ \
| * \ 61b6a7c Merge from release/1.1.x
| |\ \
|/ / /
| | _
| * f2d9b5e (tag: v1.1.2) Merge from fix/fix-2
| |\
| | * f95ccbe fix: my bug fix 2 on release/1.1.x
| |/
* | 99a3662 (tag: v1.2.0) Merge from feature/awesome-3
|\ \
| |/
|/|
| * cc6c1ed feat: my awesome 3rd change
|/
* 145cbff (tag: v1.1.1) Merge from bug/fix-1
|\
| * 681a11b fix: my bug fix on main
|/
* e9e7644 (tag: v1.1.0) Merge from feature/awesome-2
|\
| * f30042e feat: my awesome 2nd change
|/
* fba50a2 (tag: v1.0.0, tag: v0.2.0) Merge from feature/awesome-1
|\
| * bf05218 feat: my awesome change
|/
* c619bff (tag: v0.1.1) fix(doc): fix documentation
* 128a5d9 (tag: v0.1.0) feat: add README.md
gsemver bump major
gsemver bump minor
gsemver bump patch
All the CLI options are documented here.
NOTE
When you specify a CLI option for the bump command, it overrides the whole configuration if defined. See bellow.
Since v0.8.0, it can extract the version from a go module tag.
Example: if your last tag is foo/v1.2.0
, it will use v1.2.0
to calculate the next version and return a version in the form of vX.Y.Z
without the module prefix.
You can also use a configuration file to define your own rules.
By default it will look for a file in .gsemver.yaml
or then in $HOME/.gsemver.yaml
but you can specify your own configuration file thanks to the --config
(or -c
) option:
gsemver --config my-config.yaml
# or
gsemver -c my-config.yaml
The configuration file format looks like:
majorPattern: "(?:^.+\!:.*$|(?m)^BREAKING CHANGE:.*$)"
minorPattern: "^(?:feat|chore|build|ci|refactor|perf)(?:\(.+\))?:.*$"
bumpStrategies:
- branchesPattern: "^(main|master|release/.*)$"
strategy: "AUTO"
preRelease: false
preReleaseTemplate:
preReleaseOverwrite: false
buildMetadataTemplate:
- branchesPattern: ".*"
strategy: "AUTO"
preRelease: false
preReleaseTemplate:
preReleaseOverwrite: false
buildMetadataTemplate: "{{.Commits | len}}.{{(.Commits | first).Hash.Short}}"
This is the default configuration used for Conventional Commits. You can adapt the configuration to your needs.
The bumpStrategies
are applied in order until one matches the branchesPattern
regular expression with the current branch.
This allows you to define your strategies based on your own git flow.
For the API usage, you can check the godoc where there are some examples.
You can also check version bumper release which is used to release gsemver itself.
We are always welcoming your contribution 👏
But to make everyone's work easier, please read the CONTRIBUTING guide first.
I would like to make gsemver
a better tool and take more scenario into account and eventually non conventional commits log.
Therefore, your feedback is very useful.
I am very happy to hear your opinions on Issues and PR ❤️