How to increase the version number?
Tags: development, devop, go, javascript
Published: 2022-06-15 13:24:14

---
title: How to increase the version number? 
published: true
description: small go program to change versions in a file.
tags: #development #devop #go #javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mw67biwdqbkv3zpidl4f.png
---

## Abstract

The article is about writing the following small program.

The result can be downloaded [here](https://gitlab.schukai.com/oss/utilities/version/-/packages/147).

```bash
version init 
# 0.1.0
echo "1.0.0" | version patch 
# 1.0.1
echo "1.0.0" | version minor
# 1.1.0
echo "1.0.0" | version major
# 2.0.0
```

---

## Small Side Project On Sunday

There are tasks that I do over and over again and am really never satisfied with the results. But often I don't manage to do it cleanly.

I think every developer has such tasks. 

<img width="100%" style="width:100%" src="https://media.giphy.com/media/o6yTNi1n1huxshqI5F/giphy-downsized-large.gif">

Some tasks don't actually stand out and others really bug you. 

I would like to briefly describe here how you can come up with working solutions relatively quickly using prefabricated libraries and some glue.
 
One of my minor pains is continually changing the version in the project. A good introduction to version numbers is described on the SemVer page. I have noted all the links below.

<img width="100%" style="width:100%" src="https://media.giphy.com/media/W2KcfZICjiFtdBOMvF/giphy.gif">

The first question is always what problem is there to solve.

For JavaScript, I often use `npm version`, for C or Go projects a combination of shell script or manual adjustment. 

Example of a bash script:

```bash
#!/bin/bash

### Increments the part of the string
## $1: version itself
## $2: number of part: 0 – major, 1 – minor, 2 – patch
increment_version() {
  local delimiter=.
  local array=($(echo "$1" | tr $delimiter '\n'))
  array[$2]=$((array[$2] + 1))
  if [ $2 -lt 2 ]; then array[2]=0; fi
  if [ $2 -lt 1 ]; then array[1]=0; fi
  echo $(
    local IFS=$delimiter
    echo "${array[*]}"
  )
}

JSON="${1}"
VERSION="${2}"
MODE="${3:-1}"

jq '.version="'$(increment_version "${VERSION}" "${MODE}")'"' "${JSON}" > "${JSON}.new"
rm "${JSON}"
mv "${JSON}.new" "${JSON}"
```

The manual way is of course always possible, especially for small projects. However, as soon as I use a CI tool, I want to automate this in some way. 

After all, it's not a task that's really challenging. On Sunday, I felt like writing a little tool here. As a finger exercise, so to speak.

My own program is nothing more or less than the linking of excellent libraries. 

I love to write something like this in go because it makes a nice little program without any big runtime dependencies. 

With JavaScript or php, you always need the runtime environment.

In which language do you write such tools? Leave a comment.

## Shoulder of giants

Now you have to identify the individual challenges and finally see which subtask there is to solve.

Basically, I had to solve the following tasks:

- Reading the version number from a file (json and yaml).
Alternatively, via standard input or directly from a git tag
- Bump one of the three parts of the version number
- Output the new version number again, write it to the file or set a git tag.

Here I would like to present them briefly without going into more detail. If you know of a good library, post a comment. I'm constantly looking for good libraries.

Reading files is quite easy with Go. For Json, Yaml and Git there are good libraries.

### JSON

`GJson` and `SJson` seem to be great libraries to read or write values in a JSON. The libraries make it easy.

```go
json, _ := os.ReadFile("my.json")
value := gjson.Get(string(content), "x.y.z")
fmt.Println(value.String())
```

### YAML

I have not found anything similar for YAML. But here you can work quite well with the YAML nodes and operate directly on them. With the library `yaml-jsonpath` you can work there directly.

```go
yaml, _ := os.ReadFile("my.yaml")
var node yaml.Node
yaml.Unmarshal(content, &node)
path, _ := yamlpath.NewPath('x.y.z')
results, _ := path.Find(&node)
result := results[0]
fmt.Println(result.Value)
```

### Version

For working with the version number, the library `semver` is recommended. It can actually do everything you need.

```go
version, _ := semver.NewVersion("1.2.3-beta.1+build345")
next := version.IncMajor()
```

### Git

If you work with Git tags, you want to read out the last tag, change it and set it again. The version can be retrieved via console or CI as follows:

```bash
git fetch --tags
NEXT=$(git tag --sort=-v:refname | grep -E "[0-9]+\.[0-9]+\.[0-9]+" | head -n 1 | version patch)

git tag -a "$NEXT" -m "version $NEXT"
git push --follow-tags

```

But I also wanted to integrate that somehow.

For working with a Git repository, the library `go-git` is worth highlighting. `go-git` is a highly extensible git implementation library written in pure Go.

```go
repo, _ := git.PlainOpen(path)
h, _ := repo.Head()
repo.CreateTag(version, h.Hash(), &git.CreateTagOptions{
		Message: version,
	})
```

## The Result

I put the whole thing together with tape and some glue and compiled it. The program is available for [download](http://download.schukai.com/tools/version/) and the source code can also be viewed.

So my finished program can now read and change version number from standard input.

```bash
echo "1.0.0" | version patch 
# 1.0.1
echo "1.0.0" | version minor
# 1.1.0
echo "1.0.0" | version major
# 2.0.0
```

Or change the version number in a file

```bash
version patch --path my.json --selector go.to.version
```

Or directly set a new version as Git Tag

```bash
version patch --git
```

### Go

In go you can include the version in the finished program via the linker. In the go program we create a variable. Here with the name version. The name is freely selectable.

```go
package main

var {
  version string
}

```



```bash

## bump version
version patch --path version.json --selector "version"
export VERSION=$(cat version.json | jq -r .version)

## set version
go build -ldflags "-X main.version=$(VERSION)" 

``` 

Voila!

## Epilog

Like I said, it's not great cinema, but maybe it will help you tackle these types of tasks on your next project.


Another note on dependencies. Libraries are good, because you save time and possibly get a better solution. But you have to look very carefully here. It can happen quickly that you get malicious code.

Update 2023-07-16

The program can now also read commit messages directly and derive a new version number from them. 

Update 2023-07-30

The `version' program can now make predictions about what the next version will be via git without setting it. [Read more](https://dev.to/volker_schukai/version-management-made-easy-a-look-at-the-versioning-tool-28af)


### References

- https://github.com/go-git/go-git
- https://github.com/Masterminds/semver
- https://github.com/tidwall/sjson
- https://github.com/tidwall/gjson
- https://github.com/vmware-labs/yaml-jsonpath
- https://gitlab.schukai.com/oss/utilities/version/-/packages/147
- https://github.com/git/git/blob/master/Documentation/RelNotes/1.8.3.txt#L155-L156
- https://semver.org/
- https://www.schukai.com/
- https://docs.npmjs.com/about-semantic-versioning
- https://docs.npmjs.com/cli/v8/commands/npm-version
- https://github.com/c4urself/bump2version/#installation
- https://gitlab.schukai.com/oss/utilities/version/-/tree/master