Coder Social home page Coder Social logo

git-semver's Introduction

git-semver: Semantic Versioning with git tags

What is this used for?

  • CI/CD pipeline: Continuously version your artifacts and uniquely identify your dev-builds. git-semver will generate unique SemVer compliant versions for each commit you add to your project. Let's say you were using APP_VERSION in your build-pipeline you could simply replace it with
    $ APP_VERSION=$(git-semver)
  • Tag releases: Automate your workflow for tagging releases of your software. Automatically select the next patch/minor/major version when creating a new release tag:
    $ git tag $(git-semver -target minor)
    or create an alias
    alias gtg-min="git tag -s -a $(git-semver -target minor)"  

Why is this useful?

Software should be versioned in order to be able to identify a certain feature set or to know when a specific bug has been fixed. It is a good practice to use Semantic Versioning (SemVer) in order to attach a meaning to a version number or the change thereof.

git allows you to conveniently reference a certain state of your code through the usage of tags. Tags can have an arbitrary identifier, so that it seems a natural choice to use them for versioning.


Version tags

A semantic version consists of three dot-separated parts <major>.<minor>.<patch> and this should be the name that you give to a tag. Optionally you can prepend the letter v if your language specific tooling requires it. It is also possible to attach a pre-release identifier to a version e.g. for a release candidate. This identifier is separated with hyphen from the core version component. A valid version tag would be, e.g. 1.2.3, v2.3.0, 1.1.0-rc3.

$ git tag v2.0.0-rc1

So for a tagged commit we would know which version to assign to our software, but which version should we use for not tagged commits? We can use git describe to get a unique identifier based on the last tagged commit.

$ git describe --tags
3.5.1-22-gbaf822dd5

This is the 22nd commit after the tag 3.5.1 with the abbreviated commit hash gbaf822dd5. Sadly this identifier has two drawbacks.

  1. It's not compliant to SemVer, because there are multiple hyphens after the core version. See the BNF specification

  2. It doesn't allow proper sorting of versions, because the pre-release identifier would make the version smaller than the tagged version, even though it has several commits build on top of that version.

Usage

git-semver collects information about the head commit of a repo similar to how git describe would do it and derives a SemVer compliant version from it. E.g.:

git describe git-semver
3.5.1-22-gbaf822d 3.5.2-dev.22+baf822dd
4.2.0-rc.3-5-gfcf2c8f 4.2.0-rc.3.dev.5+fcf2c8fd
1.0.1 1.0.1

It will attach a pre-release tag of the form dev.N, where N is the number of commits since the last commit, and the commit hash as build-metadata. Additionally the patch level component will be incremented in case of a pre-release-version. If the last tag itself contains a pre-release-identifier the dev.N suffix will be appended but all other parts will be left untouched. This complies with the precedence rules defined in the SemVer spec. So that

0.9.9 < 1.0.0-rc.1 < 1.0.0-rc1.dev.3+fcf2c8fd < 1.0.0-rc.2 < 1.0.0

Formatting

The output of git-semver can be controlled with the -format option or one of it shorthand companions as described here. The format string can include the following characters

Format char Description
x Major version
y Minor version
z Patch version
p Pre-release version
m Metadata

The format chars x, y and z are separted with a dot, p with a hyphen and m with a plus character. A valid format string is e.g.: x.y+m

Command line options

The output and parsing of git-semver can be controlled with the following options.

Name Description
-format Format string as described here
-no-minor Exclude minor version and all following components
-no-patch Exclude patch version and all following components
-no-pre Exclude pre-release version and all following components
-no-meta/-no-hash Exclude build metadata
-prefix Prefix string for version e.g.: v
-set-meta Set buildmeta to this value
-guard Ignore shorthand formats for pre-release versions
-target Set target release dev(default), patch, minor or major

Examples

$ git-semver
3.5.2-dev.22+8eaec5d3

# Exclude build metadata
$ git-semver -no-meta
3.5.2-dev.22

# Only major and minor version
$ git-semver -no-patch
3.5

$ git-semver -prefix v -no-hash
v3.5.2

$ git-semver -set-meta custom
3.5.2+custom

$ git-semver -target minor
3.6.0

$ git-semver -target major
4.0.0

Bumping versions

A common application of git-semver is to create new

Release safeguard

If you use git-semver to automatically derive versions for your application (e.g. in a CI/CD environment), and you want to provide convenient shorthand versions (e.g. 1.2), so that it is easier to follow non-breaking updates, you might run into the problem that a pre-release version accidentally overwrites a production version. This is because

# tag of HEAD commit: 1.2.2
$ git-semver -no-patch
1.2

# tag of HEAD commit: 1.2.3-dev.1"
$ git-semver -no-patch
1.2

result in the same shorthand version. To mitigate this problem you can use the -guard option that will ignore any output format that doesn't contain the pre-release identifier if the current version is a pre-release version. E.g.

# tag of HEAD commit: 1.2.3-dev.1"
$ git-semver -guard -no-patch
1.2.3-dev.1+8eaec5d3

Caveats

If you create multiple annotated tags on the same commit (e.g. you want to promote a release candidate to be the final release without adding any further commits), git-semver will pick the tag that was created last, which is usually what you want. E.g.

$ git tag -a -m "Release candidate" 1.1.0-rc.1 
$ git-semver
1.1.0-rc.1
$ git tag -a -m "Final release" 1.1.0
$ git-semver
1.1.0

Installation

Currently, git-semver can be installed with go install

$ go install github.com/mdomke/git-semver/v6@latest

There is also a Homebrew formula that can be installed with

$ brew install mdomke/git-semver/git-semver

Docker usage

You can also use git-semver as a docker-container. Images are available from DockerHub and GitHub Container Registry

$ docker run --rm -v `pwd`:/git-semver mdomke/git-semver

or

$ docker run --rm -v `pwd`:/git-semver ghcr.io/mdomke/git-semver

git-semver's People

Contributors

birchb1024 avatar choffmeister avatar ckoehn avatar flood4life avatar masonkatz avatar mdomke avatar schorzz avatar yobeonline avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

git-semver's Issues

highest tag not used for generating semver

If there are more then one tag placed on the current version, git-semver will not use the highest semantic version.

E.g. both tags 0.0.1-rc1 and 0.0.1.rc2 placed on current version.

This is most likely due to git describe --tags also reporting only the older tag.

Please either find a solution for this or simply document this behaviour in the documentation.

Strip leading characters in git tag

Hi there, thanks for making this tool.

We create our git tags with a leading v i.e. v1.2.3.

I am creating automatic helm releases in CI and git-semver includes the leading v as our tags include this.

The leading v is actually not SemVer compliant. Would it be possible for you to add an option to strip any leading characters?

docker run reports '0.0.0', docker image likely stale

Neat tool! But the docker run command in your readme seems to be broken. 2bb1e6967c61 is the image hash.

$ docker run --rm -v ${PWD}:/git-semver mdomke/git-semver

0.0.0

Expected:
$ docker run --rm -v ${PWD}:/git-semver -w /git-semver alpine/git describe

v0.0.2-9-g64dbbb8

BUT, if I force the workdir, it seems to work fine:

$ docker run --rm -v ${PWD}:/git-semver -w /git-semver mdomke/git-semver

0.0.5-dev1+g64dbbb8

$ docker inspect mdomke/git-semver | grep -i work

"WorkingDir": "",

But if I rebuild the image from latest master, "WorkingDir": "/git-semver",. So I think your dockerhub image is just stale.

branch name in the metadata

Hi

It would be quite useful if there could be a possibility to have a branch name somewhere in the metadata as well.

For not breaking back compatibility, it could be implemented as a placeholder for a custom format (for example x.y.z+b-m)

Add --match option to match a tag pattern (like git describe --match <pattern>)

It would be very useful to have an option to only consider tags that match a pattern. Like git describe --match <pattern> does.
This would allow a usecase where you might have a file VERSION-TAG in your repo and you call git-semver as:
git-semver --no-prefix --match $(cat VERSION-TAG)
This would allow better branch management of your repo.
Here is a simplified scenario:

          D---E---F
         /
A---B---C---G---H---I
  • A is tagged v0.1.0-alpha and has VERSION-TAG as v0.1.0-alpha
  • B's version is 0.1.0-alpha.dev.1+meta
  • C is tagged with both v1.0.0-rc and v1.1.0-alpha. C's commit also changes VERSION-TAG to v1.0.0 (this will match the v1.0.0-rc tag).
  • D's commit changes VERSION-TAG to v1.1.0-alpha. D's version is 1.1.0-alpha.dev.1+meta. F's version is 1.1.0-alpha.dev.3+meta
  • G's versoin is 1.0.0-rc.dev.1+meta. H's version is 1.0.0-rc.dev.2+meta
  • I is tagged as v1.0.0 and I's version is 1.0.0

For commit tagged with release and pre-release tags, pre-release takes precedence

It is pretty typical to tag a single commit as a release candidate, and then, after testing, tag that same commit as the final release version. When I do this and use the git-semver docker image to get the version, it reports the rc version, not the release version. See example below.

$ mkdir test-2
$ cd test-2
$ git init
Initialized empty Git repository in ***/test-2/.git/
$ touch README
$ git add README
$ git commit -m "Initial commit"
[main (root-commit) 0bd0fac] Initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README
$ git tag -s "1.0.0-rc.1" -m "1.0.0-rc.1"
$ docker run -v {$PWD}:/git-semver mdomke/git-semver:6.3.1
1.0.0-rc.1
$ touch info.txt
$ git add info.txt
$ git commit -m "Added some file"
[main be7c724] Added some file
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 info.txt
$ docker run -v {$PWD}:/git-semver mdomke/git-semver:6.3.1
1.0.0-rc.1.dev.1+be7c724e
$ git tag -s "1.0.0-rc.2" -m "1.0.0-rc.2"
$ docker run -v {$PWD}:/git-semver mdomke/git-semver:6.3.1
1.0.0-rc.2
$ git tag -s "1.0.0" -m "1.0.0"
$ docker run -v {$PWD}:/git-semver mdomke/git-semver:6.3.1
1.0.0-rc.2

MacOS 12.3.1
git 2.32.0
docker 20.10.12

Incorrect version since switch to go-git

$ git init
$ git commit --allow-empty -m 'Initial commit'
$ git tag -a -s 0.0.1-alpha.1 -m 'Initial release'
$ git checkout -b feature/some-branch
$ git commit --allow-empty -m 'First commit in feature branch'
$ git tag -a -s 0.0.2-alpha.1 -m 'Awesome feature'
$ git checkout master
$ git merge feature/some-branch

$ git describe
0.0.2-alpha.1-1-gea647de
$ git-semver
0.0.1-alpha.1.dev.2+ea647de9

Windows release

We use this tool for our CI, but we need it as well on windows. Is there a chance this will be released anytime soon?

git-semver fails if launched from a subfoler of the repository

Description

Git-semver fails when launched from a subdirectory, wherease git describe succeeds.

Steps to reproduce

mkdir -p test/foo
cd test
git init
touch foo/bar
git add foo/bar
git commit -m"init"
git tag -a 0.1.0 -m"release"
cd foo
git describe
# 0.1.0
git-semver
# failed to open repo: repository does not exist

Fix

I will PR later, but I think the problem lies here. According to this post, you should call git.PlainOpenWithOptions instead of git.PlainOpen. The option structure is declared here and should have the attribute DetectDotGit set to true.

dev prefix prevents proper sorting

An example of a version generated by this tool is 3.5.2-dev22+gbaf822dd5. This has a problem as https://semver.org/#spec-item-9 defines, that non numeric prerelease identifiers are to be sorted as text. That is

3.5.2-dev22+gbaf822dd5 < 3.5.2-dev3+gbaf822dd5 (dev22 must be compared with dev3)

To fix this the dev prefix and the number should be split by a dot, so that the correct order

3.5.2-dev.3+gbaf822dd5 < 3.5.2-dev.22+gbaf822dd5 (the parts 3 and 22 are pure numeric, hence compared as number and not as strings)

would apply.

Incrementation differs between rcN and rc.N

when adding a tag 1.0.1-rc.3 and checking out the following commit, the resulting version reads:

1.0.1-rc.3.dev1+gb1c06d6

but replacing the tag with one that omits the period 1.0.1-rc3 the result reads:

1.0.1-rc4.dev1+gb1c06d6

where the automatic incrementation of the rcN clashes with the reference rules, once I define an rc4 further up the branch

Is this behavior on purpose?
(i realize that the period is proper SemVer, but we're running without it in our software so I'm somewhat stuck there)

Add flags to control which component should be bumped for prereleases.

I don't know if it makes sense to anyone else or if it is against best practices, but I wish I could customize which version component (major, minor, patch) should be bumped.

Examples

  • git-semver would give 3.5.2-dev.22+8eaec5d3
  • git-semver -minor would give 3.6.0-dev.22+8eaec5d3
  • git-semver -major would give 4.0.0-dev.22+8eaec5d3

format with pre-release number x.y.z.p

I am trying to format with

git-semver -format x.y.z.p

to get a 4 digit number

e.g. if the tag is 1.2.3-dev

I would like to have the number
1.2.3.0
and on the next commit to get
1.2.3.1

How could I achieve that?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.